Architecture
Nomos is a small multi-module Maven project: a runtime that generated code depends on, a core generator library, a Maven plugin that drives it, and a BOM for version alignment.
Modules
| Module | Responsibility |
|---|---|
nomos-bom | Bill of Materials — pins dependency versions for consumers |
nomos-runtime | Runtime that generated code depends on: the JSON model (dev.cjfravel.nomos.json), the template type model, codecs, and the validator + registries |
nomos-core | The generator: template parser, code generator, template serializer, and the Nomos facade |
nomos-maven-plugin | Scans templates and invokes the core to generate sources at build time |
nomos-example | End-to-end consumer used as an integration test |
Generation pipeline
- TemplateParser reads JSON into the type model, resolving
$refvariants, and validating and rendering default literals. Missing-field defaults are applied by the generateddecode. - CodeGenerator walks the model to emit case classes, companions, enum files, and
NomosFormats. - FileWriter writes the generated files, skipping any whose contents are byte-identical (so unchanged files keep their timestamps), and prunes orphaned generated files left behind by removed definitions.
- TemplateSerializer renders the model back into Scala source — this is what gets embedded for runtime validation.
- MultiValidator validates JSON against a
MultiTemplate, resolving references across definitions.
The type model
A MultiTemplate holds project settings and a list of TemplateDefinitions, each with a TemplateType:
sealed trait TemplateType
// StringType, NumberType, IntType, LongType, DecimalType, BooleanType,
// DateType, DateTimeType, ArrayType, MapType, UnionType, ObjectType,
// ReferenceType, RecursiveRef, ExternalType, EnumType, TypeDiscriminator
Object and discriminator field order is preserved with ListMap, so generated case-class fields match the template.
Template embedding
Rather than ship a schema file, the generator serializes the MultiTemplate into
the generated NomosFormats object and builds a validator from it. This keeps the
model and its validation in one artifact and guarantees they agree — there is no second copy
to drift.
Adding a feature
A new template capability usually touches the same five seams, in this order:
- model — add or extend a
TemplateType/ field. - parser — recognize the new template syntax.
- generator — emit the corresponding Scala.
- validator — enforce it at runtime.
- serializer — round-trip it into the embedded template.
Write the failing test first, thread the change through these seams, then prove it compiles and runs in nomos-example.
Back to Contributing.