<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Koine | Blog</title><description>Koine is a domain-specific language for Domain-Driven Design: write the ubiquitous language once in .koi files and generate idiomatic, self-contained C# (and TypeScript) — value objects, entities, aggregates, invariants, commands, events, state machines, CQRS and context maps.</description><link>https://atypical-consulting.github.io/</link><language>en</language><item><title>Modeling money: value objects and invariants in Koine</title><link>https://atypical-consulting.github.io/Koine/blog/modeling-money-value-objects-and-invariants/</link><guid isPermaLink="true">https://atypical-consulting.github.io/Koine/blog/modeling-money-value-objects-and-invariants/</guid><description>Money is the canonical value object: equal by value, never negative, immutable. Here&apos;s how you say that in Koine, and the guard-clause C# you get back.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; is the example everyone reaches for when explaining value objects, and for good reason. It’s
defined entirely by its values (€10 &lt;em&gt;is&lt;/em&gt; €10), it’s immutable, and it carries a rule that must always
hold: an amount can’t be negative. That makes it the perfect first thing to model in Koine.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;a-value-object-is-its-values&quot;&gt;A value object is its values&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Here’s &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; and the smart enum it depends on:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt; Billing {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; Currency(&lt;/span&gt;&lt;span&gt;symbol&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;decimals&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EUR(&lt;/span&gt;&lt;span&gt;&quot;€&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;USD(&lt;/span&gt;&lt;span&gt;&quot;$&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;GBP(&lt;/span&gt;&lt;span&gt;&quot;£&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; Money {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;:   &lt;/span&gt;&lt;span&gt;Decimal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;currency&lt;/span&gt;&lt;span&gt;: Currency&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;invariant&lt;/span&gt;&lt;span&gt; amount &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&quot;a monetary amount cannot be negative&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Three things are happening:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;value Money&lt;/code&gt;&lt;/strong&gt; declares a value object. Its identity is the tuple of its fields — two &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt;
instances with the same amount and currency are equal, full stop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code dir=&quot;auto&quot;&gt;Currency&lt;/code&gt; is a smart enum&lt;/strong&gt; whose members carry constant data. &lt;code dir=&quot;auto&quot;&gt;EUR&lt;/code&gt; isn’t just a name; it knows
its &lt;code dir=&quot;auto&quot;&gt;symbol&lt;/code&gt; and how many &lt;code dir=&quot;auto&quot;&gt;decimals&lt;/code&gt; it has.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &lt;code dir=&quot;auto&quot;&gt;invariant&lt;/code&gt;&lt;/strong&gt; states a rule in domain language, with the message a user will see if it’s
violated.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;the-c-you-get-back&quot;&gt;The C# you get back&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;koine build&lt;/code&gt; emits &lt;code dir=&quot;auto&quot;&gt;Billing/ValueObjects/Money.cs&lt;/code&gt; (output is nested by category — value objects go
under &lt;code dir=&quot;auto&quot;&gt;ValueObjects/&lt;/code&gt;, enums under &lt;code dir=&quot;auto&quot;&gt;Enums/&lt;/code&gt;). The shape is the same one you’d write by hand — a sealed
class, a validating constructor, by-value equality:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Money&lt;/span&gt;&lt;span&gt; : ValueObject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;decimal&lt;/span&gt;&lt;span&gt; Amount { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; Currency Currency { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Money&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;decimal&lt;/span&gt;&lt;span&gt; amount, Currency currency)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (amount &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; DomainInvariantViolationException(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;nameof&lt;/span&gt;&lt;span&gt;(Money),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rule: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;a monetary amount cannot be negative&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Amount &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; amount;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Currency &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; currency;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ToString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&quot;&lt;/span&gt;&lt;span&gt;Money {{ Amount = &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Amount&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;, Currency = &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Currency&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; }}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; IEnumerable&amp;#x3C;&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;?&gt; &lt;/span&gt;&lt;span&gt;GetEqualityComponents&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Amount;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Currency;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The guard is &lt;strong&gt;inverted&lt;/strong&gt;: the emitter negates &lt;code dir=&quot;auto&quot;&gt;amount &gt;= 0&lt;/code&gt; to &lt;code dir=&quot;auto&quot;&gt;amount &amp;#x3C; 0&lt;/code&gt; and always wraps it in
braces — the constructor fails fast with the literal message you wrote in the model.&lt;/p&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;Currency&lt;/code&gt; field type is the smart enum generated alongside Money into
&lt;code dir=&quot;auto&quot;&gt;Billing/Enums/Currency.cs&lt;/code&gt;. Here’s a representative excerpt:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/// &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A type-safe smart enum: static instances with value equality.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Currency&lt;/span&gt;&lt;span&gt; : IEquatable&amp;#x3C;Currency&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; Currency EUR &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;EUR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;€&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; Currency USD &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;USD&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; Currency GBP &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;GBP&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;£&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; Name { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; Value { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; Symbol { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; Decimals { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Currency&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; name, &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; value, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; symbol, &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; decimals)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Name &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; name;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Value &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Symbol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; symbol;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Decimals &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; decimals;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; IReadOnlyList&amp;#x3C;Currency&gt; All { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;[] { EUR, USD, GBP };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; Currency &lt;/span&gt;&lt;span&gt;FromName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; name)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;All&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;FirstOrDefault&lt;/span&gt;&lt;span&gt;(e &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; name)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; ArgumentOutOfRangeException(&lt;/span&gt;&lt;span&gt;nameof&lt;/span&gt;&lt;span&gt;(name), &lt;/span&gt;&lt;span&gt;$&quot;&lt;/span&gt;&lt;span&gt;No Currency with name &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos;.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// … TryFromName / FromValue / TryFromValue …&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; TResult &lt;/span&gt;&lt;span&gt;Match&lt;/span&gt;&lt;span&gt;&amp;#x3C;TResult&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Func&amp;#x3C;TResult&gt; eUR,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Func&amp;#x3C;TResult&gt; uSD,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Func&amp;#x3C;TResult&gt; gBP)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; Value &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eUR&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;uSD&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gBP&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; InvalidOperationException(&lt;/span&gt;&lt;span&gt;$&quot;&lt;/span&gt;&lt;span&gt;Unhandled Currency &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos;.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// … Switch / ToString / Equals / GetHashCode / == / != …&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;EUR&lt;/code&gt; isn’t just a name: it carries &lt;code dir=&quot;auto&quot;&gt;Symbol = &quot;€&quot;&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Decimals = 2&lt;/code&gt;. The &lt;code dir=&quot;auto&quot;&gt;Match&lt;/code&gt; signature is
exhaustive — the compiler forces you to handle every member. This is the class that &lt;code dir=&quot;auto&quot;&gt;Money.Currency&lt;/code&gt;
references; &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; carries a &lt;code dir=&quot;auto&quot;&gt;Currency&lt;/code&gt; reference, not a raw string or integer.&lt;/p&gt;
&lt;p&gt;The key move is &lt;strong&gt;where&lt;/strong&gt; the invariant lands: it’s a guard at the &lt;em&gt;top&lt;/em&gt; of the constructor, before any
field is assigned. There is no way to construct a negative &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt;. The object is valid by the time it
exists, or it doesn’t exist — the rule isn’t a method you have to remember to call, it’s a property of
the type. Equality comes from the &lt;code dir=&quot;auto&quot;&gt;ValueObject&lt;/code&gt; base (a small emitted marker, not a NuGet package):
&lt;code dir=&quot;auto&quot;&gt;GetEqualityComponents&lt;/code&gt; lists the fields that define identity, and the base supplies &lt;code dir=&quot;auto&quot;&gt;Equals&lt;/code&gt;,
&lt;code dir=&quot;auto&quot;&gt;GetHashCode&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;==&lt;/code&gt;/&lt;code dir=&quot;auto&quot;&gt;!=&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;derived-fields-computed-not-stored&quot;&gt;Derived fields: computed, not stored&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Often a value object exposes something computed from its other fields. Say you want a normalized SKU:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; Sku {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;:       &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;normalized&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; code.trim.upper&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;invariant&lt;/span&gt;&lt;span&gt; code.trim.length &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;               &lt;/span&gt;&lt;span&gt;&quot;a SKU cannot be blank&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;invariant&lt;/span&gt;&lt;span&gt; code &lt;/span&gt;&lt;span&gt;matches&lt;/span&gt;&lt;span&gt; /^[A-Z]{3}-[0-9]{4}$/&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;SKU must look like ABC-1234&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A field with an &lt;code dir=&quot;auto&quot;&gt;= expression&lt;/code&gt; that references other fields is &lt;strong&gt;derived&lt;/strong&gt;. It does &lt;em&gt;not&lt;/em&gt; become a
constructor parameter — you can’t pass it in — it becomes a get-only computed property:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; Normalized &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Code&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Trim&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ToUpperInvariant&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That distinction is the whole point: &lt;code dir=&quot;auto&quot;&gt;code&lt;/code&gt; is input you supply; &lt;code dir=&quot;auto&quot;&gt;normalized&lt;/code&gt; is a consequence the
model computes. And the second invariant — &lt;code dir=&quot;auto&quot;&gt;matches /…/&lt;/code&gt; — compiles straight to &lt;code dir=&quot;auto&quot;&gt;Regex.IsMatch&lt;/code&gt;, so a
malformed SKU is rejected at construction time just like a negative amount.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-this-is-the-part-worth-getting-right&quot;&gt;Why this is the part worth getting right&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Value objects are where a domain model earns its keep. Push the rules down into the type and the rest
of the system gets simpler for free: an application service that accepts a &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; never has to
re-check that it’s non-negative, because a negative &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; is unrepresentable. Koine makes stating
those rules cheap — one &lt;code dir=&quot;auto&quot;&gt;invariant&lt;/code&gt; line — and turns them into guard clauses you can read and trust.&lt;/p&gt;
&lt;p&gt;From here, &lt;a href=&quot;https://atypical-consulting.github.io/Koine/reference/value-objects/&quot;&gt;value objects in the reference&lt;/a&gt; covers quantities (a
&lt;code dir=&quot;auto&quot;&gt;Decimal&lt;/code&gt; plus a unit, with unit-checked arithmetic), &lt;code dir=&quot;auto&quot;&gt;Range&amp;#x3C;T&gt;&lt;/code&gt; intervals, and more derived-field
patterns. Or open &lt;a href=&quot;https://atypical-consulting.github.io/Koine/studio/&quot;&gt;Koine Studio&lt;/a&gt;, tweak the &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; invariant, and
watch the guard clause change as you type.&lt;/p&gt;</content:encoded><category>tutorial</category><category>value-objects</category><category>invariants</category></item><item><title>One model, many targets: how Koine stays language-agnostic</title><link>https://atypical-consulting.github.io/Koine/blog/one-model-many-targets/</link><guid isPermaLink="true">https://atypical-consulting.github.io/Koine/blog/one-model-many-targets/</guid><description>The same Money model, two languages, one flag. Keeping C# out of the semantic model is what makes a second target a new emitter instead of a rewrite.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A domain model isn’t a C# thing. “A &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; is an amount and a currency, and the amount can’t be
negative” is true regardless of the language you ship it in. Koine takes that seriously: the parser
and the semantic model are kept &lt;strong&gt;strictly target-agnostic&lt;/strong&gt;, and no C# concept is allowed to leak
into them. The payoff is that adding a language is writing a new &lt;em&gt;emitter&lt;/em&gt;, not rewriting the compiler
— and you can prove it by flipping one flag.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-same-model-a-different-flag&quot;&gt;The same model, a different flag&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Here’s a &lt;code dir=&quot;auto&quot;&gt;Billing&lt;/code&gt; context with a smart enum and a value object:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt; Billing {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; Currency(&lt;/span&gt;&lt;span&gt;symbol&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;decimals&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EUR(&lt;/span&gt;&lt;span&gt;&quot;€&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;USD(&lt;/span&gt;&lt;span&gt;&quot;$&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;GBP(&lt;/span&gt;&lt;span&gt;&quot;£&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; Money {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;:   &lt;/span&gt;&lt;span&gt;Decimal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;currency&lt;/span&gt;&lt;span&gt;: Currency&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;invariant&lt;/span&gt;&lt;span&gt; amount &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&quot;a monetary amount cannot be negative&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Build it for C#:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;koine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;billing.koi&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--target&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;csharp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--out&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./generated&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;and &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; lands at &lt;code dir=&quot;auto&quot;&gt;Billing/ValueObjects/Money.cs&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Money&lt;/span&gt;&lt;span&gt; : ValueObject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;decimal&lt;/span&gt;&lt;span&gt; Amount { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; Currency Currency { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Money&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;decimal&lt;/span&gt;&lt;span&gt; amount, Currency currency)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (amount &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; DomainInvariantViolationException(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;nameof&lt;/span&gt;&lt;span&gt;(Money),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rule: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;a monetary amount cannot be negative&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Amount &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; amount;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Currency &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; currency;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ToString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&quot;&lt;/span&gt;&lt;span&gt;Money {{ Amount = &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Amount&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;, Currency = &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Currency&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; }}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; IEnumerable&amp;#x3C;&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;?&gt; &lt;/span&gt;&lt;span&gt;GetEqualityComponents&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Amount;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Currency;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now change one word — &lt;code dir=&quot;auto&quot;&gt;--target typescript&lt;/code&gt; — and the &lt;em&gt;same&lt;/em&gt; model compiles to
&lt;code dir=&quot;auto&quot;&gt;Billing/value-objects/Money.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// &amp;#x3C;auto-generated/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { DomainInvariantViolationError, Decimal, ValueObject } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../../runtime&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { CurrencyMember } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../enums/Currency&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Money&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ValueObject&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; amount&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Decimal&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; currency&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CurrencyMember&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;constructor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Decimal&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;currency&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;CurrencyMember&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;(amount&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;compareTo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Decimal&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DomainInvariantViolationError&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Money&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;a monetary amount cannot be negative&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; amount;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;currency&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; currency;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;equalityComponents&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt;[] {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;currency&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Same fields, same invariant, same value-equality semantics — expressed idiomatically for each
language. The model didn’t change; only the emitter did.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;idiomatic-means-different-on-purpose&quot;&gt;Idiomatic means &lt;em&gt;different&lt;/em&gt;, on purpose&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A naïve “transpiler” would emit C#-shaped TypeScript. Koine’s emitters instead lean into each
language’s conventions, which is why the two outputs aren’t line-for-line translations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The runtime.&lt;/strong&gt; C# gets a &lt;code dir=&quot;auto&quot;&gt;Koine/Runtime/&lt;/code&gt; folder of marker types (&lt;code dir=&quot;auto&quot;&gt;ValueObject&lt;/code&gt;,
&lt;code dir=&quot;auto&quot;&gt;DomainInvariantViolationException&lt;/code&gt;, …) emitted only when used. TypeScript gets a single
&lt;code dir=&quot;auto&quot;&gt;runtime.ts&lt;/code&gt; — and because JavaScript’s &lt;code dir=&quot;auto&quot;&gt;number&lt;/code&gt; is lossy for money, that runtime ships a
string-backed &lt;code dir=&quot;auto&quot;&gt;Decimal&lt;/code&gt; class, so &lt;code dir=&quot;auto&quot;&gt;Money.amount&lt;/code&gt; is a &lt;code dir=&quot;auto&quot;&gt;Decimal&lt;/code&gt;, not a &lt;code dir=&quot;auto&quot;&gt;number&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The exception.&lt;/strong&gt; C# throws &lt;code dir=&quot;auto&quot;&gt;DomainInvariantViolationException&lt;/code&gt;; idiomatic TypeScript throws a
&lt;code dir=&quot;auto&quot;&gt;DomainInvariantViolationError&lt;/code&gt; (the &lt;code dir=&quot;auto&quot;&gt;…Error&lt;/code&gt; suffix is the JS convention).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The guard.&lt;/strong&gt; The C# emitter simplifies &lt;code dir=&quot;auto&quot;&gt;amount &gt;= 0&lt;/code&gt; to the inverted guard &lt;code dir=&quot;auto&quot;&gt;if (amount &amp;#x3C; 0)&lt;/code&gt;;
the TypeScript emitter keeps the explicit &lt;code dir=&quot;auto&quot;&gt;if (!(amount.compareTo(new Decimal(&apos;0&apos;)) &gt;= 0))&lt;/code&gt;,
routed through &lt;code dir=&quot;auto&quot;&gt;Decimal.compareTo&lt;/code&gt;. Same rule, two natural spellings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The smart enum.&lt;/strong&gt; In C#, &lt;code dir=&quot;auto&quot;&gt;Currency&lt;/code&gt; becomes a &lt;code dir=&quot;auto&quot;&gt;sealed class : IEquatable&amp;#x3C;Currency&gt;&lt;/code&gt; with static
instances. In TypeScript it becomes a &lt;code dir=&quot;auto&quot;&gt;CurrencyMember&lt;/code&gt; interface plus a &lt;code dir=&quot;auto&quot;&gt;const Currency = { … }&lt;/code&gt;
object and free functions (&lt;code dir=&quot;auto&quot;&gt;CurrencyFromName&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;CurrencyMatch&lt;/code&gt;, …) — the shape a TS developer
expects. That’s why &lt;code dir=&quot;auto&quot;&gt;Money.currency&lt;/code&gt; is typed &lt;code dir=&quot;auto&quot;&gt;Currency&lt;/code&gt; in C# but &lt;code dir=&quot;auto&quot;&gt;CurrencyMember&lt;/code&gt; in TS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Even the folders.&lt;/strong&gt; C# uses &lt;code dir=&quot;auto&quot;&gt;Billing/ValueObjects/…&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Billing/Enums/…&lt;/code&gt;; the TypeScript
emitter uses lower-case, kebab-case &lt;code dir=&quot;auto&quot;&gt;Billing/value-objects/…&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Billing/enums/…&lt;/code&gt;, plus a
&lt;code dir=&quot;auto&quot;&gt;tsconfig.json&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these choices live in the model. They live in their respective emitters.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-the-boundary-is-worth-defending&quot;&gt;Why the boundary is worth defending&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;.koi source&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ ANTLR lexer/parser&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ semantic model        ← target-agnostic; knows nothing about C# or TS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ semantic validator&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ IEmitter              ← C#, TypeScript, Python, PHP, glossary&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ idiomatic source files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Everything above the emitter line is shared. Parsing, name resolution, type checking, and every
diagnostic happen once, against a model that has no opinion about output languages. An emitter is just
an &lt;code dir=&quot;auto&quot;&gt;IEmitter&lt;/code&gt; that walks that model and writes text. So a new target inherits all the validation and
correctness work for free — it only has to answer “what does &lt;em&gt;this&lt;/em&gt; construct look like in &lt;em&gt;my&lt;/em&gt;
language?”&lt;/p&gt;
&lt;p&gt;That discipline is also why &lt;a href=&quot;https://atypical-consulting.github.io/Koine/studio/&quot;&gt;Koine Studio&lt;/a&gt; can show C#, TypeScript, and a
glossary side by side from one model: it’s the same compile, three emitters. C# is the most complete
target today; TypeScript is emitting now, &lt;strong&gt;Python&lt;/strong&gt; (Phase 1: tactical core — value objects,
smart enums, identity entities, events, and repository protocols, all dependency-free and
&lt;code dir=&quot;auto&quot;&gt;mypy --strict&lt;/code&gt;-clean) and &lt;strong&gt;PHP 8.1&lt;/strong&gt; (the tactical core &lt;em&gt;and&lt;/em&gt; the strategic/CQRS layer — read models,
query handler seams, application services/use cases/operations, specifications, policies, context-map
ACL translators and integration-event subscriber seams — dependency-free, using typed
properties and readonly promoted properties) are available today via the CLI and MCP server. A &lt;strong&gt;Rust&lt;/strong&gt;
target ships too (Phase 1: an idiomatic crate — smart constructors returning &lt;code dir=&quot;auto&quot;&gt;Result&amp;#x3C;_, DomainError&gt;&lt;/code&gt;,
exhaustively-matched enums, repository &lt;code dir=&quot;auto&quot;&gt;trait&lt;/code&gt;s, proved by a &lt;code dir=&quot;auto&quot;&gt;cargo check&lt;/code&gt; meta-test). Your existing
&lt;code dir=&quot;auto&quot;&gt;.koi&lt;/code&gt; models compile to every target without a single edit — because they never described C# in the
first place.&lt;/p&gt;
&lt;p&gt;Want to see it live? Open &lt;a href=&quot;https://atypical-consulting.github.io/Koine/studio/&quot;&gt;Koine Studio&lt;/a&gt; and flip the
target dropdown between C# and TypeScript — same model, recompiled on the spot.&lt;/p&gt;</content:encoded><category>architecture</category><category>code-generation</category><category>typescript</category></item><item><title>Why Koine generates code instead of shipping a framework</title><link>https://atypical-consulting.github.io/Koine/blog/why-generate-code-not-ship-a-framework/</link><guid isPermaLink="true">https://atypical-consulting.github.io/Koine/blog/why-generate-code-not-ship-a-framework/</guid><description>A base library makes your domain depend on the framework forever. A compiler hands you plain C# and then gets out of the way. We picked the compiler.</description><pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There are two ways to help someone write less Domain-Driven Design boilerplate. You can ship a
&lt;strong&gt;framework&lt;/strong&gt; — base classes like &lt;code dir=&quot;auto&quot;&gt;ValueObject&amp;#x3C;T&gt;&lt;/code&gt;, an &lt;code dir=&quot;auto&quot;&gt;AggregateRoot&lt;/code&gt;, a &lt;code dir=&quot;auto&quot;&gt;Result&amp;#x3C;T&gt;&lt;/code&gt; — that they
inherit from and depend on at runtime. Or you can ship a &lt;strong&gt;compiler&lt;/strong&gt; that reads a model and emits the
boilerplate as ordinary source code they own.&lt;/p&gt;
&lt;p&gt;Koine is firmly in the second camp. This post is about why.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-cost-of-a-base-class-framework&quot;&gt;The cost of a base-class framework&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A runtime library is the obvious choice, and it has real benefits: it’s familiar, it’s a single NuGet
reference, and updates arrive by bumping a version. But it also bakes in costs that compound over the
life of a system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A permanent dependency.&lt;/strong&gt; Your &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; &lt;em&gt;is-a&lt;/em&gt; &lt;code dir=&quot;auto&quot;&gt;FrameworkValueObject&lt;/code&gt;. That coupling never goes
away. Upgrading the framework is a domain-wide event; abandoning it is a rewrite.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Magic at runtime.&lt;/strong&gt; Equality, validation, and serialization often lean on reflection or
convention. When something misbehaves, you’re debugging the framework’s internals, not your code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An abstraction you can’t see.&lt;/strong&gt; The most important code in your system — the rules that define
what a valid &lt;code dir=&quot;auto&quot;&gt;Order&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; — lives behind an inherited member you have to trust rather than read.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these are fatal. But for the layer that’s supposed to be the clearest in the whole codebase,
they’re exactly the wrong trade.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-a-compiler-hands-you-instead&quot;&gt;What a compiler hands you instead&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Koine reads a &lt;code dir=&quot;auto&quot;&gt;.koi&lt;/code&gt; model and writes the C# you would have written by hand. The output has &lt;strong&gt;no
external dependencies&lt;/strong&gt;. A small set of marker types (&lt;code dir=&quot;auto&quot;&gt;ValueObject&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;IAggregateRoot&lt;/code&gt;,
&lt;code dir=&quot;auto&quot;&gt;DomainInvariantViolationException&lt;/code&gt;, …) is emitted into a &lt;code dir=&quot;auto&quot;&gt;Koine/Runtime/&lt;/code&gt; folder &lt;strong&gt;only when your
model actually uses them&lt;/strong&gt; — a model with no events never gets &lt;code dir=&quot;auto&quot;&gt;IDomainEvent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So a value object with a couple of invariants and a derived field compiles to exactly this — no
base library to inherit, no attributes, no reflection:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// &amp;#x3C;auto-generated/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;nullable enable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Collections&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Generic&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Text&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;RegularExpressions&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Koine&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Runtime&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Customers&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Email&lt;/span&gt;&lt;span&gt; : ValueObject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; Raw { &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Email&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; raw)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;raw&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Trim&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Length&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; DomainInvariantViolationException(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;nameof&lt;/span&gt;&lt;span&gt;(Email),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rule: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;an email cannot be blank&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;Regex&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IsMatch&lt;/span&gt;&lt;span&gt;(raw, &lt;/span&gt;&lt;span&gt;@&quot;&lt;/span&gt;&lt;span&gt;^[^@]+@[^@]+\.[^@]+$&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; DomainInvariantViolationException(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;nameof&lt;/span&gt;&lt;span&gt;(Email),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rule: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;invalid email address&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Raw &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; raw;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; Normalized&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Raw&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Trim&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ToLowerInvariant&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ToString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&quot;&lt;/span&gt;&lt;span&gt;Email {{ Raw = &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Raw&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; }}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; IEnumerable&amp;#x3C;&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;?&gt; &lt;/span&gt;&lt;span&gt;GetEqualityComponents&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Raw;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can read every line. You can step through it in a debugger without leaving your own assembly. You
can review it in a pull request — and because codegen is &lt;strong&gt;deterministic&lt;/strong&gt;, the same model always
produces byte-identical output, so a meaningful diff means a meaningful &lt;em&gt;model&lt;/em&gt; change. There’s no
hidden behavior because there’s no framework underneath; the marker base class is a small,
dependency-free file you can open and read too.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;a-green-build-proves-the-domain&quot;&gt;”A green build proves the domain”&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The strongest argument for generating code is what it lets you guarantee. Every construct Koine emits
is covered two ways in the test suite:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Snapshot tests&lt;/strong&gt; pin the exact generated text, so an unintended change to the output is caught as
a diff.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Roslyn meta-test&lt;/strong&gt; compiles the emitted C# in-memory &lt;em&gt;and runs it&lt;/em&gt;, asserting that invariants
throw, equality works by value, and state machines reject illegal transitions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When &lt;code dir=&quot;auto&quot;&gt;dotnet test&lt;/code&gt; is green, the generated C# isn’t just plausible — it’s been compiled and executed.
That’s a much stronger claim than a framework can make about the code you write on top of it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-part-that-matters-most-leverage&quot;&gt;The part that matters most: leverage&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Keeping C# out of the semantic model isn’t only architectural tidiness — it’s what makes the output
&lt;em&gt;portable&lt;/em&gt;. The parser and semantic model don’t know what C# is. The emitter does. Add another
emitter and the same model compiles to another language for free: TypeScript, Python, and PHP
targets already ship, and Rust is on the roadmap. A framework can’t do that; its base classes &lt;em&gt;are&lt;/em&gt; the target.&lt;/p&gt;
&lt;p&gt;Generating code costs you a build step. In return you get output you own, output you can read, output
that’s been proven by execution, and a model that outlives any single target language. For the
ubiquitous language — the one part of the system that should be the clearest — that’s a trade worth
making.&lt;/p&gt;
&lt;p&gt;Want to see the output for yourself? &lt;a href=&quot;https://atypical-consulting.github.io/Koine/start/reading-the-output/&quot;&gt;Reading the generated C#&lt;/a&gt;
walks one value object from &lt;code dir=&quot;auto&quot;&gt;.koi&lt;/code&gt; to &lt;code dir=&quot;auto&quot;&gt;.cs&lt;/code&gt;, line by line.&lt;/p&gt;</content:encoded><category>architecture</category><category>code-generation</category><category>domain-driven-design</category></item><item><title>Introducing Koine: a language for your domain</title><link>https://atypical-consulting.github.io/Koine/blog/introducing-koine/</link><guid isPermaLink="true">https://atypical-consulting.github.io/Koine/blog/introducing-koine/</guid><description>You already write the ubiquitous language down — in tickets, in diagrams, in a wiki nobody reads. Koine makes it the source code, and compiles the boilerplate for you.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every Domain-Driven Design project starts with the same promise: the &lt;strong&gt;ubiquitous language&lt;/strong&gt; is the
single source of truth. A &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; value object cannot be negative; an &lt;code dir=&quot;auto&quot;&gt;Order&lt;/code&gt; in &lt;code dir=&quot;auto&quot;&gt;Draft&lt;/code&gt; has no
lines; an email must look like an email. Everyone agrees. Then the language gets written down three
times — in a wiki, in a class diagram, and (eventually, partially, drifting) in C# — and the three
copies start disagreeing the moment the first deadline lands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Koine&lt;/strong&gt; removes two of those copies. You write the bounded context’s ubiquitous language &lt;strong&gt;once&lt;/strong&gt;,
in a small readable DSL, and the compiler emits the idiomatic C# for you: value objects, entities,
aggregates, invariants, commands, events, state machines, repositories, the application/CQRS layer,
and context maps.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-five-minute-version&quot;&gt;The five-minute version&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Here is a slice of a &lt;code dir=&quot;auto&quot;&gt;Billing&lt;/code&gt; context — a value object with an invariant, a regex-validated email,
a smart enum, and an entity with identity:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt; Billing {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; Money {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;:   &lt;/span&gt;&lt;span&gt;Decimal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;currency&lt;/span&gt;&lt;span&gt;: Currency&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;invariant&lt;/span&gt;&lt;span&gt; amount &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;a monetary amount cannot be negative&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; Currency { EUR, USD, GBP }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; Email {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;raw&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;invariant&lt;/span&gt;&lt;span&gt; raw &lt;/span&gt;&lt;span&gt;matches&lt;/span&gt;&lt;span&gt; /^[^@]+@[^@]+$/&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&quot;invalid email address&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;entity&lt;/span&gt;&lt;span&gt; Customer &lt;/span&gt;&lt;span&gt;identified&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; CustomerId {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:  &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;: Email&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Run &lt;code dir=&quot;auto&quot;&gt;koine build billing.koi --target csharp --out ./generated&lt;/code&gt; and you get a predictable, nested tree:
one folder per context, sub-folders by category (&lt;code dir=&quot;auto&quot;&gt;ValueObjects/&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;Enums/&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;Entities/&lt;/code&gt;, …), plus a
tiny &lt;code dir=&quot;auto&quot;&gt;Koine/Runtime/&lt;/code&gt; folder with the shared marker types. For example, &lt;code dir=&quot;auto&quot;&gt;Money&lt;/code&gt; lands at
&lt;code dir=&quot;auto&quot;&gt;Billing/ValueObjects/Money.cs&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Currency&lt;/code&gt; at &lt;code dir=&quot;auto&quot;&gt;Billing/Enums/Currency.cs&lt;/code&gt; — namespaces stay flat
(&lt;code dir=&quot;auto&quot;&gt;namespace Billing;&lt;/code&gt;), only the folders are categorised. The emitted &lt;code dir=&quot;auto&quot;&gt;Money.cs&lt;/code&gt; is a
&lt;code dir=&quot;auto&quot;&gt;sealed class : ValueObject&lt;/code&gt; whose constructor throws &lt;strong&gt;before&lt;/strong&gt; an invalid instance can exist — no
NuGet dependency, no reflection, nothing to learn. It’s the C# you’d have written by hand on a good
day, every time.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-a-language-not-a-library&quot;&gt;Why a &lt;em&gt;language&lt;/em&gt;, not a library&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Plenty of tools give you a DDD base library — a &lt;code dir=&quot;auto&quot;&gt;ValueObject&amp;#x3C;T&gt;&lt;/code&gt;, a &lt;code dir=&quot;auto&quot;&gt;Result&lt;/code&gt;, an aggregate base class.
They help, but they leave the actual modeling work (and all the boilerplate) to you, and they put a
runtime dependency between your domain and the framework.&lt;/p&gt;
&lt;p&gt;Koine takes the opposite bet. The &lt;code dir=&quot;auto&quot;&gt;.koi&lt;/code&gt; file is the model; the C# is &lt;strong&gt;output&lt;/strong&gt;, not a contract you
implement against. That has three consequences worth the trade:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The model reads like the domain.&lt;/strong&gt; &lt;code dir=&quot;auto&quot;&gt;invariant amount &gt;= 0 &quot;a monetary amount cannot be negative&quot;&lt;/code&gt;
is a sentence a domain expert can check. The generated guard clause is a detail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The output is yours.&lt;/strong&gt; It’s plain, dependency-free C# you can read, review in a pull request, and
commit to git. Delete Koine tomorrow and the generated code still compiles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A green build proves the domain.&lt;/strong&gt; Every emitted type is snapshot-tested &lt;em&gt;and&lt;/em&gt; compiled-and-run
through an in-memory Roslyn meta-test. When the test suite is green, the generated C# is correct.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;how-the-pipeline-works&quot;&gt;How the pipeline works&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Koine is strictly layered, and the parser and semantic model are kept &lt;strong&gt;target-agnostic&lt;/strong&gt; on purpose:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;.koi source&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ ANTLR lexer/parser&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ semantic model        (no C# concepts live here)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ semantic validator    (diagnostics with line/column)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ emitter               (C#, TypeScript, Python, PHP)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ idiomatic source files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Because no C# concept leaks into the semantic model, adding a second target is “write another
emitter,” not “rewrite the compiler.” C# ships today, alongside TypeScript, Python, and PHP emitters,
with Rust on the roadmap.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;try-it-without-installing-anything&quot;&gt;Try it without installing anything&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The whole compiler runs in your browser as a WebAssembly module. Open the
&lt;a href=&quot;https://atypical-consulting.github.io/Koine/studio/&quot;&gt;Koine Studio&lt;/a&gt;, edit the model, and watch it recompile to C# the moment you stop
typing — no install, no server.&lt;/p&gt;
&lt;p&gt;When you’re ready to go deeper, &lt;a href=&quot;https://atypical-consulting.github.io/Koine/start/what-is-koine/&quot;&gt;What is Koine?&lt;/a&gt; walks through the
pipeline, and &lt;a href=&quot;https://atypical-consulting.github.io/Koine/start/your-first-model/&quot;&gt;your first model&lt;/a&gt; gets you to generated code in a few
minutes. Welcome to Κοινή — the common tongue of your domain.&lt;/p&gt;</content:encoded><category>announcement</category><category>domain-driven-design</category><category>dsl</category></item></channel></rss>