Feature catalogue
This is the everything-at-a-glance page: every construct Koine ships — the full tactical and strategic
toolkit plus the developer tooling — with the short .koi syntax for it, the C# (or Markdown) it emits,
and a pointer into the canonical
pizzeria demo (which compiles the
templates/pizzeria
template). Tables are grouped by capability. Each family links to its reference page for the full story.
Tactical building blocks
Section titled “Tactical building blocks”The core DDD vocabulary: types, fields, invariants, and the expression language. See value objects, entities and identity, aggregates, invariants, and expressions.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Value object | value Price { amount: Decimal } | sealed record with get-only props, validating ctor, value equality | Money, Topping, Address, Coupon, Discount |
| Entity + id | entity Product identified by ProductCode { … } | sealed class with identity-only equality + a generated id value object | Pizza, Order, KitchenTicket, Delivery, Charge |
| Aggregate root | aggregate Order root Order { entity Order … } | nested types in the context namespace; root implements IAggregateRoot; an I<Root>Repository is emitted | Order, Catalog (root Pizza), Dispatch (root Delivery), Billing (root Charge), Books (root LedgerEntry), Kitchen.Line.KitchenTicket |
| Typed field | name: Type | a typed property + ctor parameter | every type |
| Defaulted field | status: OrderStatus = Draft | a ctor parameter with a default value | Order.status, KitchenTicket.stage |
| Derived field | lineTotal: Money = price * quantity | a get-only computed property (not in the ctor) | OrderLine.lineTotal, Order.total |
| Range invariant | invariant amount >= 0 "…" | a ctor guard that throws DomainInvariantViolationException | Money.amount >= 0, OrderLine.quantity >= 1 |
| Regex invariant | invariant code matches /…/ "…" | a Regex.IsMatch guard | Coupon.code, Address.postalCode |
| Conditional invariant | invariant status == Draft when lines.isEmpty | if (cond && !body) throw | Order draft rule |
| Conditional expression | if cond then a else b | a C# ternary | OrderLine.payable |
| String ops | code.trim.upper, a + b | .Trim(), .ToUpperInvariant(), string concat | Coupon.normalized, Address.formatted |
| Collection ops + lambdas | lines.sum(l => l.lineTotal), .count, .all, .distinctBy | LINQ (.Sum, .Count, .All, .DistinctBy); pulls using System.Linq; | Order.total, Order invariants |
| Multiple contexts → namespaces | context Catalog { … } | one C# namespace + folder per context | all six contexts |
Optionality, sets & docs
Section titled “Optionality, sets & docs”See value objects and contexts and types.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Optional field | description: String? | a nullable property; supports ?? and .isPresent | Pizza.description/kcal, KitchenTicket.startedAt |
| Set | tags: Set<String> | IReadOnlySet<T> (de-duplicated in the ctor) | Pizza.toppings |
| Doc comment | /// summary text | a C# XML <summary> on the member/type | ordering.koi, menu.koi, payment.koi |
| Glossary | koine build … --glossary pizzeria.md | a Markdown glossary grouped by context (each heading shows its version) then type | the --glossary flag |
Commands, events & state
Section titled “Commands, events & state”| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Command | command submit() { requires …; status -> Placed; emit … } | a mutating method that checks requires, applies field -> value transitions, re-checks invariants | Order.place/cancel, Delivery.pickUp/depart/complete, Charge.capture/refund |
| Command returning a value | command cancel(): OrderId { …; result id } | a typed method (public <T> Name(…)) that returns the result expression as its terminal statement (the create-and-return-id idiom) | — |
| Domain event | event OrderSubmitted { … } + emit OrderSubmitted(…) | a record recorded into the root’s DomainEvents collection | OrderOpened, OrderPlacedInternally, DeliveryScheduled, ChargeAuthorized |
| State machine | states { Draft -> Placed; … } | runtime-checked legal transitions; illegal transition throws | Order, Delivery, Charge, KitchenTicket lifecycles |
Factories
Section titled “Factories”The aggregate’s only public construction path. See factories.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Named factory | create open(customer: CustomerId, …) { … } | public static <Entity> Open(…): generate id, check requires, construct with named ctor args, emit, return | Order.open, Delivery.schedule, Charge.authorize |
| Field init | total -> lines.sum(l => l.price) | a named ctor argument total: <expr> | factory bodies |
| Auto-bind | create open(customer: CustomerId, …) (param name = field) | binds the matching field without an explicit -> | Order.open |
| Creation event | emit OrderOpened(orderId: id, …) | records the event into DomainEvents after construction | factory bodies |
| Factory-only construction | (presence of any create) | the all-args constructor becomes private | every aggregate with a factory |
Richer value objects
Section titled “Richer value objects”See enums and value objects.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Smart enum | enum OrderStatus { Draft, Placed, Shipped } | sealed class with static instances, Name/Value, All, FromName/FromValue, value equality, ==/!= | every enum |
| Enum with associated data | enum Currency(symbol: String, decimals: Int) { EUR("€", 2) } | each signature field becomes a get-only PascalCase property | Currency(symbol, decimals) |
| Quantity | quantity Weight { amount: Decimal unit: MassUnit } | a value object with unit-checked +/- (throws on mixed units) and scalar *// that preserve the unit | Portion |
Range<T> | window: Range<Instant> | the runtime Koine/Runtime/Range.cs (Contains, Overlaps, start≤end guard); element must be Int, Decimal, or Instant | HappyHour.window |
Specifications, services & policies
Section titled “Specifications, services & policies”See specs, services & policies.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Specification | spec IsVip on Customer = … | an extension-method predicate bool IsVip(this Customer x) in <Context>Specifications.cs; call as customer.IsVip(); reusable in invariants | Promotions.IsFreeOrder (on Discount) |
| Domain service (pure) | service LoyaltyService { operation discountRate(…): Decimal = … } | a sealed class with one expression-bodied method per operation | Promotions.DiscountService |
| Domain service (seam) | service Calc { operation run(a: Int): Int } | an abstract class with abstract method seams | (any bodyless operation) |
| Policy | policy PostToLedger when ChargeCaptured then Books.record(…) | IPostToLedgerPolicy + an abstract PostToLedgerPolicy seam (the reaction is a doc sketch, not executed code) | Payment.PostToLedger |
Identity, repositories & concurrency
Section titled “Identity, repositories & concurrency”See repositories & concurrency and entities and identity.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Guid identity (default) | identified by OrderId | a Guid-backed id value object with a New() generator | most aggregates |
| Natural key | identified by ProductCode as natural(String) | a String/Int-backed id, value equality, no New(); blanks rejected | Pizza (PizzaCode) |
| Sequence identity | identified by InvoiceNo as sequence | a long-backed id, no New() (the store assigns it) | sequence ids |
| Repository interface | (any aggregate root) | I<Root>Repository with GetByIdAsync/AddAsync/UpdateAsync/RemoveAsync | every aggregate |
| Repository operations + finders | repository { operations: add, getById find byCustomer(…): List<Order> } | tunes the mutating set; find → async …Async (list → IReadOnlyList<>, single → Root?) | Ordering: byCustomer, mostRecent |
| Optimistic concurrency | aggregate Order root Order versioned { … } | a get-only Version; Koine.Runtime.ConcurrencyConflictException on stale writes | Order |
Application layer & CQRS
Section titled “Application layer & CQRS”See application layer & CQRS and the application-layer tutorial.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Unit of Work | (≥1 aggregate in a context) | <Context>/IUnitOfWork.cs with one I<Root>Repository property per aggregate (pluralized) + SaveChangesAsync | Payment.IUnitOfWork (Billing + Books), Menu, Ordering |
| Application service | service OrderingService { usecase PlaceOrder(…): OrderId } | IOrderingService with one async method per use case (Task/Task<T>; List<T> params → IReadOnlyList<T>) | Ordering.IOrderingService |
| Read model + projection | readmodel OrderSummary from Order { id customer lineCount: Int = lines.count } | a sealed record + a static ToOrderSummary(this Order src) projection mapper | Menu.MenuItem, Ordering.OrderSummary |
| Query object | query OrdersByStatus(status: OrderStatus): List<OrderSummary> | a query DTO record + the shared Koine.Runtime.IQueryHandler<TQuery, TResult> | PizzasBySize, PizzaByCode, OrdersByStatus |
Multi-file, imports & modules
Section titled “Multi-file, imports & modules”See multi-file, imports & modules and the multiple contexts tutorial.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Directory compilation | koine build templates/pizzeria/ … | every *.koi under the directory merges into one model | the whole templates/pizzeria folder |
| Named import | import Menu.{ Topping } | a precise using Menu;; names usable unqualified | Kitchen imports Menu.{ Topping } |
| Wildcard import | import Menu.* | resolves all exported names from the context | (companion form) |
| Qualified reference | address: Menu.Topping | a fully-qualified C# type, no using added | cross-context refs |
| Module | module Line { … } | a <Context>.<Module> sub-namespace + sub-folder | Kitchen.Line |
Context maps & integration events
Section titled “Context maps & integration events”See context maps & integration events and the multiple contexts tutorial. The map is enforced and drives emission.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Context map | contextmap { Menu -> Kitchen : conformist } | no type by itself; validates and permits cross-context references | context-map.koi (8 relationships) |
| Relation roles | partnership, shared-kernel, customer-supplier, conformist, anti-corruption-layer, open-host, published-language | each role gates references/subscriptions differently | the map relations |
| Shared kernel | Menu <-> Ordering : shared-kernel { Currency } | the shared type emitted once into Menu__Ordering/Kernel/; partners get a precise using | Currency |
| Anti-corruption layer | Gateway -> Payment : anti-corruption-layer + acl { Gateway.GatewayResult -> Payment.PaymentReceipt } | a translator interface IGatewayToPaymentTranslator in the downstream context | Gateway -> Payment |
| Integration event | integration event OrderPlaced { … } | a sealed record : IIntegrationEvent; the marker is emitted once | Ordering.OrderPlaced, Kitchen.TicketReady |
| Publish | publishes OrderPlaced | marks the event as a published surface; authorizes subscribers | Ordering, Kitchen |
| Subscribe | subscribes Ordering.OrderPlaced | an IHandleOrderPlaced handler interface with the fully-qualified event type | Kitchen, Delivery, Payment |
A context may even subscribe to two same-named events from different publishers (e.g. both
Sales.Shipped and Returns.Shipped). The bare IHandleShipped seam would collide, so each colliding
handler is qualified by its publisher — IHandleSalesShipped and IHandleReturnsShipped, each typed
on its own publisher’s event. The common single-publisher case keeps the bare IHandle<Event> name
unchanged. The same publisher-qualification applies in the TypeScript and PHP emitters; Python emits no
per-subscription handler seam, so its same-named events simply live in their distinct context packages.
Model versioning & evolution
Section titled “Model versioning & evolution”See model versioning and the evolving a model tutorial.
| Construct | .koi syntax (short) | Emits | Demo location |
|---|---|---|---|
| Context version | context Catalog version 2 { … } | metadata only (glossary heading + @since ceiling check); byte-identical C# | Menu version 2 |
@since(n) | @since(2) barcode: String? | no C#; surfaces in the glossary as since v2; warns (KOI1501) if above the context version | Pizza.kcal @since(2) |
@deprecated("reason") | @deprecated("use amount") legacyAmount: Decimal | [Obsolete("reason")] on the property/class + using System; | deprecation markers |
| Backward-compat check | koine check v2 --baseline v1 | compares published surfaces; exits non-zero on breaking changes | examples/versioning/ |
Developer tooling
Section titled “Developer tooling”Not language constructs, but the commands and editor support that make .koi pleasant to write. See
the CLI reference and editor tooling.
| Tool | Invocation | What it does | Reference |
|---|---|---|---|
| Formatter | koine fmt <path> [--check] | Canonically, idempotently reformats .koi in place; --check verifies without writing (CI gate) | CLI |
| Project scaffold | koine init [dir] [--force] | Writes a buildable starter domain.koi, koine.config, and README.md; --force overwrites | CLI |
| Watch mode | koine watch <path> [--out <dir>] | Re-emits (or re-validates) on every .koi change with debounced fast feedback | CLI |
| Language server | koine lsp | LSP over stdio: live diagnostics, hover, completion, and cross-file go-to-definition | Editor tooling |
| TextMate grammar | (editor extension) | Syntax highlighting for .koi in VS Code and Rider | Editor tooling |
See also
Section titled “See also”- What is Koine? — the one-page pitch.
- Reference overview — the language reference index.
- Reading the output — how the emitted C# is laid out.