<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: hello-ediflow</title>
    <description>The latest articles on Forem by hello-ediflow (@helloediflow).</description>
    <link>https://forem.com/helloediflow</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3744812%2F9ffd2db6-a51b-4773-bc11-bb11ac1b4acb.png</url>
      <title>Forem: hello-ediflow</title>
      <link>https://forem.com/helloediflow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/helloediflow"/>
    <language>en</language>
    <item>
      <title>Building EDIFlow - Presentation Layer: CLI, DI Container &amp; Wiring (Part 5)</title>
      <dc:creator>hello-ediflow</dc:creator>
      <pubDate>Mon, 11 May 2026 13:00:38 +0000</pubDate>
      <link>https://forem.com/helloediflow/building-ediflow-presentation-layer-cli-di-container-wiring-part-5-28m1</link>
      <guid>https://forem.com/helloediflow/building-ediflow-presentation-layer-cli-di-container-wiring-part-5-28m1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Series:&lt;/strong&gt; Building EDIFlow - A Clean Architecture Journey in TypeScript (Part 5/6)&lt;br&gt;
&lt;strong&gt;Reading Time:&lt;/strong&gt; ~8 minutes&lt;/p&gt;


&lt;h2&gt;
  
  
  Recap — Where We Left Off
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/helloediflow"&gt;Part 4&lt;/a&gt;, we implemented the Infrastructure Layer — EDIFACT/X12 parsers, builders, validators, the file-based repository, and 13 data packages with 126–319 message definitions each.&lt;/p&gt;

&lt;p&gt;Now it's time for the &lt;strong&gt;outermost layer&lt;/strong&gt; — the Presentation Layer. In EDIFlow, that's a CLI. But the patterns apply equally to a REST API, a web UI, or any other entry point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────┐
│  🔥 PRESENTATION (CLI)                      │  ← You are here
│  Commands · DI Container · Output           │
│  ┌───────────────────────────────────────┐  │
│  │  Infrastructure (Parsers, Repos)      │  │
│  │  ┌─────────────────────────────────┐  │  │
│  │  │  Application (Use Cases, Ports) │  │  │
│  │  │  ┌───────────────────────────┐  │  │  │
│  │  │  │  Domain (Entities)        │  │  │  │
│  │  │  └───────────────────────────┘  │  │  │
│  │  └─────────────────────────────────┘  │  │
│  └───────────────────────────────────────┘  │
└─────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Presentation Layer has &lt;strong&gt;one job&lt;/strong&gt;: convert user input into Use Case calls, and Use Case output into user-friendly results.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why a CLI? — The Fastest Way to Prove Your Architecture
&lt;/h2&gt;

&lt;p&gt;Why did we build a CLI instead of a REST API or a web UI?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Zero-friction developer experience.&lt;/strong&gt; A CLI lets any developer try EDIFlow in 10 seconds: &lt;code&gt;npx @ediflow/cli parse invoice.edi&lt;/code&gt;. No server setup, no browser, no configuration. Just pipe in a file and get JSON out. For an open-source library that needs adoption, this is critical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The ultimate integration test.&lt;/strong&gt; The CLI exercises every single layer — from parsing raw bytes (Infrastructure) through Use Cases (Application) to formatted output (Presentation). If &lt;code&gt;npx @ediflow/cli parse&lt;/code&gt; works, all four layers work. It's a vertical slice through the entire architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Clean Architecture makes it replaceable.&lt;/strong&gt; Because the CLI is just a thin wrapper around Use Cases, adding a REST API or a Lambda handler later is trivial — they'd call the same &lt;code&gt;UseCaseFactory&lt;/code&gt; with the same &lt;code&gt;DIContainer&lt;/code&gt;. The CLI doesn't contain business logic; it only translates command-line arguments into Use Case inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Scripting &amp;amp; CI/CD.&lt;/strong&gt; EDI processing often happens in automated pipelines — validate incoming files, convert to JSON, check against schemas. A CLI fits naturally into &lt;code&gt;bash&lt;/code&gt; scripts, GitHub Actions, and cron jobs. A web UI doesn't.&lt;/p&gt;

&lt;p&gt;In short: the CLI is the simplest possible Presentation Layer that proves Clean Architecture works end-to-end, while delivering immediate value to developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The DI Container — Where Everything Gets Wired
&lt;/h2&gt;

&lt;p&gt;This is the single place where all layers connect. In a framework like NestJS, this would be a module with providers. In EDIFlow, it's a pure TypeScript class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DIContainer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DIContainer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;useCaseFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UseCaseFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IMessageStructureRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StructureMappingService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// EDIFACT infrastructure&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;edifactParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EdifactMessageParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EdifactDelimiterDetector&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EdifactTokenizer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EdifactSegmentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;edifactBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EdifactMessageBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// X12 infrastructure&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x12Parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;X12MessageParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;X12DelimiterDetector&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;X12SegmentParser&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;X12EnvelopeParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x12Builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;X12MessageBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Register parsers and builders by standard&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EDIFACT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;edifactParser&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x12Parser&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;builders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EDIFACT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;edifactBuilder&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x12Builder&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Wire Application Layer&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EDIMessageValidationService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useCaseFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UseCaseFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;builders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validationService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileBasedMessageStructureRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DATA_PACKAGES_BASE_PATH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;structureMappingService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StructureMappingService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;DIContainer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;DIContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;DIContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DIContainer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;DIContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why a Singleton?&lt;/strong&gt; The parsers and repository don't hold mutable state between calls. Creating them once and reusing them is safe and avoids repeated initialization of data package caches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not a DI framework?&lt;/strong&gt; Because we have ~10 dependencies. A framework like &lt;code&gt;tsyringe&lt;/code&gt; or &lt;code&gt;inversify&lt;/code&gt; would add complexity for a problem that a plain constructor solves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; this is the &lt;strong&gt;only file&lt;/strong&gt; that imports from all layers simultaneously. Domain doesn't know Infrastructure. Application doesn't know Infrastructure. Only this container does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Commands — The User's Entry Point
&lt;/h2&gt;

&lt;p&gt;Each CLI command follows the same pattern: parse input → call Use Case → format output.&lt;/p&gt;

&lt;h3&gt;
  
  
  ParseCommand — The Most Complex One
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParseCommand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;useCaseFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UseCaseFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IMessageStructureRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StructureMappingService&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;program&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;file&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EDI file path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--output-type &amp;lt;type&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;edi-message | business-object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--property-parse-mode &amp;lt;mode&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code | name | camelCase | snake_case | kebab-case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readEDIFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;standard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectStandard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// UNA/UNB → EDIFACT, ISA → X12&lt;/span&gt;

    &lt;span class="c1"&gt;// Phase 1: Parse EDI → EDIMessage&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseUseCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useCaseFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createParseUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parseUseCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseStandard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ErrorHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatMultiple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Phase 2 (optional): EDIMessage → Business Object&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;business-object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;structure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessageStructure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;structure&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mappedUseCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useCaseFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createParseUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mappedUseCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseStandard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;returnTypedObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;messageStructure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;mappingKeyStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertyParseMode&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;businessObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatEDIMessageResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;detectStandard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EDIFACT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ISA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to detect EDI standard.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Auto-detection&lt;/strong&gt; — the command looks at the first characters to decide EDIFACT vs X12. No &lt;code&gt;--standard&lt;/code&gt; flag needed in most cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-phase parsing&lt;/strong&gt; — Phase 1 always runs (raw segments). Phase 2 only runs if the user wants business objects AND a data package is installed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful fallback&lt;/strong&gt; — if no data package is found, it warns and returns raw segments instead of crashing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Four Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Parse: EDI file → JSON output&lt;/span&gt;
npx @ediflow/cli parse invoice.edi
npx @ediflow/cli parse invoice.edi &lt;span class="nt"&gt;--output-type&lt;/span&gt; business-object

&lt;span class="c"&gt;# Validate: Check EDI against rules&lt;/span&gt;
npx @ediflow/cli validate invoice.edi

&lt;span class="c"&gt;# Build: JSON → EDI string&lt;/span&gt;
npx @ediflow/cli build order.json &lt;span class="nt"&gt;--standard&lt;/span&gt; edifact &lt;span class="nt"&gt;--version&lt;/span&gt; d20b &lt;span class="nt"&gt;--message&lt;/span&gt; ORDERS

&lt;span class="c"&gt;# Export Schema: Generate JSON Schema for a message type&lt;/span&gt;
npx @ediflow/cli export-schema &lt;span class="nt"&gt;--standard&lt;/span&gt; x12 &lt;span class="nt"&gt;--version&lt;/span&gt; 004010 &lt;span class="nt"&gt;--message&lt;/span&gt; 850
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each command is a class with &lt;code&gt;register()&lt;/code&gt; and &lt;code&gt;execute()&lt;/code&gt;. All injected via the DI Container.&lt;/p&gt;




&lt;h2&gt;
  
  
  Output Formatting — Supporting Multiple Formats
&lt;/h2&gt;

&lt;p&gt;The CLI supports JSON and YAML output, with an option to strip empty values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Compact JSON (default)&lt;/span&gt;
npx @ediflow/cli parse invoice.edi

&lt;span class="c"&gt;# Clean output — remove empty strings, null values, empty arrays&lt;/span&gt;
npx @ediflow/cli parse invoice.edi &lt;span class="nt"&gt;--skip-empty&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Write to file&lt;/span&gt;
npx @ediflow/cli parse invoice.edi &lt;span class="nt"&gt;-o&lt;/span&gt; result.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;OutputFormatter&lt;/code&gt; handles serialization and the &lt;code&gt;--skip-empty&lt;/code&gt; flag recursively removes noise from the output — essential when dealing with EDI messages that have hundreds of optional fields.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It All Connects — The Full Stack in One Call
&lt;/h2&gt;

&lt;p&gt;When a user runs &lt;code&gt;npx @ediflow/cli parse invoice.edi --output-type business-object&lt;/code&gt;, here's what happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI → ParseCommand
  → DIContainer.getInstance()
    → EdifactMessageParser (Infrastructure)
      → EdifactDelimiterDetector.detect()    ← reads UNA
      → EdifactTokenizer.tokenize()           ← splits segments
      → EdifactSegmentParser.parseSegment()   ← parses elements
    → ParseEDIUseCase.execute()              (Application)
      → IMessageParser.parse()               ← delegates to EDIFACT parser
      → StructureMappingService.map()         ← Phase 2: business object
    → FileBasedMessageStructureRepository    (Infrastructure)
      → loads ORDERS.json from data package
      → MessageStructureBuilder.build()
    → OutputFormatter.toJSON()               ← pretty-print result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Five layers. One call. No layer knows about the layers above or below it.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;✅ Auto-detection makes the CLI feel smart&lt;/strong&gt; — users don't need to specify the standard. The first 3 characters tell you if it's EDIFACT or X12.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Graceful degradation&lt;/strong&gt; — if a data package isn't installed, the CLI still works. It returns raw segments instead of business objects, with a helpful warning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Singleton DI Container is fine for CLI tools&lt;/strong&gt; — no request scoping needed, no concurrent state. Simple is better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Commander.js for the CLI&lt;/strong&gt; — no custom argument parsing. Commander handles flags, help text, and validation. We just define commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ The DI Container imports everything&lt;/strong&gt; — this is intentional. It's the composition root. But it means the CLI package depends on all other packages. For a library this is fine — for a microservice architecture, you'd split differently.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next — Part 6: Lessons Learned &amp;amp; The Road Ahead
&lt;/h2&gt;

&lt;p&gt;The final part of the series. What worked? What didn't? What would we do differently? And where does EDIFlow go from here?&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://dev.to/helloediflow/building-ediflow-a-clean-architecture-journey-in-typescript-part-1-why-1hhm"&gt;Part 1: Why Clean Architecture?&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://dev.to/helloediflow/building-ediflow-a-clean-architecture-journey-in-typescript-part-2-domain-layer-3g24"&gt;Part 2: Domain Layer&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://dev.to/helloediflow/building-ediflow-application-layer-use-cases-ports-factories-part-3-m46"&gt;Part 3: Application Layer&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;GitHub: @ediflow/core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ If this series helped you understand Clean Architecture in TypeScript — a &lt;strong&gt;star on GitHub&lt;/strong&gt; keeps the project going: &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;github.com/ediflow-lib/core&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Do you use a DI container or plain constructor injection in your TypeScript projects? What's your experience? Drop a comment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>cleanarchitecture</category>
      <category>opensource</category>
      <category>edi</category>
    </item>
    <item>
      <title>Building EDIFlow - Infrastructure Layer: Parsers, Repositories &amp; Data Packages (Part 4)</title>
      <dc:creator>hello-ediflow</dc:creator>
      <pubDate>Thu, 07 May 2026 03:44:18 +0000</pubDate>
      <link>https://forem.com/helloediflow/building-ediflow-infrastructure-layer-parsers-repositories-data-packages-part-4-4hjn</link>
      <guid>https://forem.com/helloediflow/building-ediflow-infrastructure-layer-parsers-repositories-data-packages-part-4-4hjn</guid>
      <description>&lt;p&gt;&lt;strong&gt;Series:&lt;/strong&gt; Building EDIFlow - A Clean Architecture Journey in TypeScript (Part 4/6)&lt;br&gt;
&lt;strong&gt;Reading Time:&lt;/strong&gt; ~12 minutes&lt;/p&gt;


&lt;h2&gt;
  
  
  Recap — Where We Left Off
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/helloediflow/building-ediflow-application-layer-use-cases-ports-factories-part-3-m46"&gt;Part 3&lt;/a&gt;, we built the Application Layer — Use Cases, Output Ports (interfaces), DTOs, and the UseCaseFactory. Everything depends on abstractions, nothing on implementations.&lt;/p&gt;

&lt;p&gt;Now it's time for the &lt;strong&gt;Infrastructure Layer&lt;/strong&gt; — where theory meets reality. This is where &lt;code&gt;IMessageParser&lt;/code&gt; becomes &lt;code&gt;EdifactMessageParser&lt;/code&gt;, where &lt;code&gt;IMessageStructureRepository&lt;/code&gt; becomes &lt;code&gt;FileBasedMessageStructureRepository&lt;/code&gt;, and where 126–319 JSON message definitions get loaded at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────────────┐
│  🔥 INFRASTRUCTURE LAYER                  │  ← You are here
│  Parsers · Builders · Repositories        │
│                                           │
│  ┌─────────────────────────────────────┐  │
│  │      Application (Use Cases, Ports) │  │
│  │  ┌───────────────────────────────┐  │  │
│  │  │      Domain (Entities)        │  │  │
│  │  └───────────────────────────────┘  │  │
│  └─────────────────────────────────────┘  │
└───────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Infrastructure in a Multi-Standard World — Why Three Packages?
&lt;/h2&gt;

&lt;p&gt;In Clean Architecture, the Infrastructure Layer implements the interfaces defined by Domain and Application. But EDIFlow supports &lt;strong&gt;four standards&lt;/strong&gt; (EDIFACT, X12, HIPAA, EANCOM) — so where does the infrastructure code live?&lt;/p&gt;

&lt;p&gt;The answer: it's split across &lt;strong&gt;three infrastructure packages&lt;/strong&gt;, each with a clear responsibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ediflow/edifact              → EDIFACT-specific: parser, builder, validator, tokenizer
@ediflow/x12                  → X12-specific: parser, builder, delimiter detection
@ediflow/infrastructure-shared → Standard-agnostic: file loading, repositories, caching
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why not one big infrastructure package?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because EDIFACT parsing and X12 parsing share &lt;strong&gt;zero implementation code&lt;/strong&gt;. The delimiters are different (&lt;code&gt;+:.'&lt;/code&gt; vs &lt;code&gt;*~&amp;gt;&lt;/code&gt;), the envelope structure is different (UNB/UNZ vs ISA/GS/ST), the escape rules are different. Putting them together would create a God-package with no cohesion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;infrastructure-shared&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This package was primarily created for the &lt;strong&gt;CLI tool&lt;/strong&gt;. The CLI needs to load message definitions for ALL standards — EDIFACT, X12, HIPAA, EANCOM — from a single entry point. The &lt;code&gt;FileBasedMessageStructureRepository&lt;/code&gt; doesn't care whether the JSON describes an EDIFACT ORDERS or an X12 850. It can't live in &lt;code&gt;@ediflow/edifact&lt;/code&gt; (X12 would depend on it) or &lt;code&gt;@ediflow/x12&lt;/code&gt; (vice versa). So it lives in a shared infrastructure package — mainly consumed by the CLI, but available to anyone who needs file-based data loading regardless of standard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The dependency graph:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ediflow/core  ←──  @ediflow/edifact
       ↑                    
       ├─────  @ediflow/x12
       ↑
       └─────  @ediflow/infrastructure-shared  ←──  @ediflow/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every infrastructure package depends on &lt;code&gt;core&lt;/code&gt; (for interfaces), never on each other. The CLI depends on all of them to wire everything together.&lt;/p&gt;

&lt;p&gt;Now let's see what happens inside each package — starting with the parsing pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Parsing Pipeline — Three Steps, Three Classes
&lt;/h2&gt;

&lt;p&gt;Parsing an EDIFACT message isn't one operation — it's a pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Raw EDI String → Delimiter Detection → Tokenization → Segment Parsing → EDIMessage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step is a separate class implementing a separate interface. Here's why, and here's the real code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Delimiter Detection
&lt;/h3&gt;

&lt;p&gt;EDIFACT messages can define custom delimiters via the &lt;code&gt;UNA&lt;/code&gt; service string. The first 9 characters tell you which characters are used for components, elements, escaping, and segment termination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactDelimiterDetector&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IDelimiterDetector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;UNA_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;UNA_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Delimiters&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasUNA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractFromUNA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// No UNA? Use EDIFACT defaults: + : . ? '&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;EdifactDelimiterDetector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEFAULT_DELIMITERS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;extractFromUNA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Delimiters&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Usually ':'&lt;/span&gt;
      &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Usually '+'&lt;/span&gt;
      &lt;span class="na"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Usually '.'&lt;/span&gt;
      &lt;span class="na"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Usually '?'&lt;/span&gt;
      &lt;span class="na"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Usually "'"&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters because real-world EDI partners sometimes use non-standard delimiters. Without this, your parser breaks on the first message from a partner who uses &lt;code&gt;*&lt;/code&gt; instead of &lt;code&gt;+&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Tokenization
&lt;/h3&gt;

&lt;p&gt;The tokenizer splits the raw string into segment strings, respecting escape characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactTokenizer&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ITokenizer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Delimiters&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentSegment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="c1"&gt;// Skip escaped characters (e.g., ?+ means literal +)&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEscapedCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;currentSegment&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consumeEscapedCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Segment terminator found — flush current segment&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentSegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentSegment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;currentSegment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;currentSegment&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why a separate class? Because X12 tokenization works differently — segments end with &lt;code&gt;~&lt;/code&gt;, and there's no escape character. Same interface (&lt;code&gt;ITokenizer&lt;/code&gt;), completely different implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: The Message Parser — Orchestrating the Pipeline
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;EdifactMessageParser&lt;/code&gt; ties everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactMessageParser&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IMessageParser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;delimiterDetector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IDelimiterDetector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ITokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;segmentParser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EdifactSegmentParser&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ParserConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;EDIMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validateMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;delimiters&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delimiterDetector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;segmentStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;segments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;segmentStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segmentParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseSegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unhSegment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unhSegment&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EDIMessageFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;messageType&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;canParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; the parser doesn't know tokenization internals. It delegates to &lt;code&gt;ITokenizer&lt;/code&gt; and &lt;code&gt;IDelimiterDetector&lt;/code&gt;. If we needed a streaming parser for huge messages, we'd swap the tokenizer — zero changes to the parser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building — The Reverse Pipeline
&lt;/h2&gt;

&lt;p&gt;Building converts &lt;code&gt;EDIMessage&lt;/code&gt; back to a raw string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactMessageBuilder&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IMessageBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;EdifactBuilderOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolveDelimiters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;OutputFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPACT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;includeUNA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUNA&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// "UNA:+.? '"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;segmentStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seg&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serializeSegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;OutputFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;READABLE&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;segmentStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;segmentStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same interface &lt;code&gt;IMessageBuilder&lt;/code&gt; — the X12 builder uses &lt;code&gt;*&lt;/code&gt; and &lt;code&gt;~&lt;/code&gt; instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validation — Builder Pattern for Format-Specific Rules
&lt;/h2&gt;

&lt;p&gt;Validation is composable. The builder lets you pick which rules to apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactValidationServiceBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ComposableValidationService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;withBasicRules&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MessageMustHaveSegmentsRule&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VersionStandardMustMatchRule&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;withEDIFACTRules&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UNBMustBeFirstRule&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UNZMustBeLastRule&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EDIFACTSegmentTagFormatRule&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;withCustomRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IValidationRule&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Factory shorthand&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;forEDIFACT&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ComposableValidationService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EdifactValidationServiceBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withBasicRules&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withEDIFACTRules&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basic rules live in &lt;code&gt;@ediflow/core&lt;/code&gt; (format-agnostic). EDIFACT rules live in &lt;code&gt;@ediflow/edifact&lt;/code&gt;. X12 rules in &lt;code&gt;@ediflow/x12&lt;/code&gt;. Each package only loads what it needs — tree-shaking friendly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Repository — Loading 126–319 Message Definitions at Runtime
&lt;/h2&gt;

&lt;p&gt;This is where the data packages come in. Each package (&lt;code&gt;@ediflow/edifact-d20b&lt;/code&gt;, &lt;code&gt;@ediflow/x12-004010&lt;/code&gt;, ...) contains JSON files that define message structures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;packages/edifact-d&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="err"&gt;b/data/&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;segments.json&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;segment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;definitions&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;elements.json&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;element&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;definitions&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;composites.json&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Composite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;element&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;definitions&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;codes/&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;values&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;messages/&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;ORDERS.json&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ORDERS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;structure&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;INVOIC.json&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;INVOIC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;structure&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;DESADV.json&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="mi"&gt;195&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;types&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;total&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;FileBasedMessageStructureRepository&lt;/code&gt; implements &lt;code&gt;IMessageStructureRepository&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileBasedMessageStructureRepository&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IMessageStructureRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;contextCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DataPackageContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getMessageStructure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageStructureDTO&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messageFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadMessageFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;messageFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrCreateContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate data package integrity&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;issues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataPackageValidationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key design decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lazy loading&lt;/strong&gt; — segments, elements, composites are loaded on first access per version, then cached&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt; — every message file is validated against the data package (segment references, element references)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package aliases&lt;/strong&gt; — HIPAA maps to &lt;code&gt;hipaa-x12-005010&lt;/code&gt;, EANCOM maps to &lt;code&gt;eancom-2002&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qualifier fallback&lt;/strong&gt; — HIPAA uses files like &lt;code&gt;837-Q1.json&lt;/code&gt; instead of &lt;code&gt;837.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Monorepo Structure — Why 13 Packages?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;packages/&lt;/span&gt;
  &lt;span class="s"&gt;core/&lt;/span&gt;                   &lt;span class="c1"&gt;# Domain + Application (pure, no parsers)&lt;/span&gt;
  &lt;span class="s"&gt;edifact/&lt;/span&gt;                &lt;span class="c1"&gt;# EDIFACT parser &amp;amp; builder&lt;/span&gt;
  &lt;span class="s"&gt;x12/&lt;/span&gt;                    &lt;span class="c1"&gt;# X12 parser &amp;amp; validator&lt;/span&gt;
  &lt;span class="s"&gt;infrastructure-shared/&lt;/span&gt;  &lt;span class="c1"&gt;# FileBasedRepository, loaders, caching&lt;/span&gt;
  &lt;span class="s"&gt;cli/&lt;/span&gt;                    &lt;span class="c1"&gt;# CLI tool (4 commands)&lt;/span&gt;
  &lt;span class="s"&gt;edifact-d96a/&lt;/span&gt;           &lt;span class="c1"&gt;# 126 EDIFACT D.96A message definitions&lt;/span&gt;
  &lt;span class="s"&gt;edifact-d01b/&lt;/span&gt;           &lt;span class="c1"&gt;# EDIFACT D.01B definitions&lt;/span&gt;
  &lt;span class="s"&gt;edifact-d12a/&lt;/span&gt;           &lt;span class="c1"&gt;# EDIFACT D.12A definitions&lt;/span&gt;
  &lt;span class="s"&gt;edifact-d20b/&lt;/span&gt;           &lt;span class="c1"&gt;# 195 EDIFACT D.20B definitions&lt;/span&gt;
  &lt;span class="s"&gt;eancom-2002/&lt;/span&gt;            &lt;span class="c1"&gt;# 50 GS1 retail messages&lt;/span&gt;
  &lt;span class="s"&gt;x12-004010/&lt;/span&gt;             &lt;span class="c1"&gt;# 293 X12 transaction sets&lt;/span&gt;
  &lt;span class="s"&gt;x12-006040/&lt;/span&gt;             &lt;span class="c1"&gt;# 319 X12 transaction sets&lt;/span&gt;
  &lt;span class="s"&gt;hipaa-x12-005010/&lt;/span&gt;       &lt;span class="c1"&gt;# 14 HIPAA transaction sets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We already covered why the infrastructure is split into &lt;code&gt;edifact&lt;/code&gt;, &lt;code&gt;x12&lt;/code&gt;, and &lt;code&gt;infrastructure-shared&lt;/code&gt; above. The &lt;strong&gt;data packages&lt;/strong&gt; follow the same principle: install only what you need. A user working with X12 004010 shouldn't download 195 EDIFACT D.20B definitions. Each data package is independent — small, focused, and &lt;code&gt;npm install&lt;/code&gt; on its own.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;✅ Pipeline pattern for parsing&lt;/strong&gt; — splitting into delimiter detection, tokenization, and segment parsing made each piece testable in isolation. When we added X12 support, we reused the pattern with different implementations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Data packages as separate npm packages&lt;/strong&gt; — users install only what they need. Keeps bundle sizes small.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Repository pattern with lazy loading&lt;/strong&gt; — loading segments.json + elements.json + composites.json was expensive (~50ms). Caching per version eliminates this on subsequent calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Builder pattern for validation&lt;/strong&gt; — format-specific rules stay in format packages. Core remains agnostic. Adding HIPAA-specific rules? Create a new builder, compose with existing rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Package aliases&lt;/strong&gt; — HIPAA and EANCOM don't follow the &lt;code&gt;{standard}-{version}&lt;/code&gt; naming convention. The alias map works but isn't elegant. Lesson: decide on naming conventions early.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next — Part 5: Presentation Layer (CLI)
&lt;/h2&gt;

&lt;p&gt;In Part 5, we'll see how the CLI ties everything together — the DI container that wires parsers + repositories + use cases, and how &lt;code&gt;parse&lt;/code&gt;, &lt;code&gt;validate&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, and &lt;code&gt;export-schema&lt;/code&gt; commands work.&lt;/p&gt;

&lt;p&gt;All already built and running in production. Part 5 walks through the real code.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://dev.to/helloediflow/building-ediflow-a-clean-architecture-journey-in-typescript-part-1-why-1hhm"&gt;Part 1: Why Clean Architecture?&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://dev.to/helloediflow/building-ediflow-a-clean-architecture-journey-in-typescript-part-2-domain-layer-3g24"&gt;Part 2: Domain Layer&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://dev.to/helloediflow/building-ediflow-application-layer-use-cases-ports-factories-part-3-m46"&gt;Part 3: Application Layer&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;GitHub: @ediflow/core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ If this series is useful — a &lt;strong&gt;star on GitHub&lt;/strong&gt; helps others find it: &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;github.com/ediflow-lib/core&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;How do you structure data packages in your monorepos? One giant package or many small ones? Drop a comment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>cleanarchitecture</category>
      <category>opensource</category>
      <category>edi</category>
    </item>
    <item>
      <title>Building EDIFlow - Application Layer: Use Cases, Ports &amp; Factories (Part 3)</title>
      <dc:creator>hello-ediflow</dc:creator>
      <pubDate>Mon, 04 May 2026 08:51:55 +0000</pubDate>
      <link>https://forem.com/helloediflow/building-ediflow-application-layer-use-cases-ports-factories-part-3-m46</link>
      <guid>https://forem.com/helloediflow/building-ediflow-application-layer-use-cases-ports-factories-part-3-m46</guid>
      <description>&lt;p&gt;&lt;strong&gt;Series:&lt;/strong&gt; Building EDIFlow - A Clean Architecture Journey in TypeScript (Part 3/6)&lt;br&gt;
&lt;strong&gt;Reading Time:&lt;/strong&gt; ~10 minutes&lt;/p&gt;


&lt;h2&gt;
  
  
  Recap — Where We Left Off
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/helloediflow/building-ediflow-a-clean-architecture-journey-in-typescript-part-2-domain-layer-3g24"&gt;Part 2&lt;/a&gt;, we built the Domain Layer — the core entities (&lt;code&gt;EDIMessage&lt;/code&gt;, &lt;code&gt;EDISegment&lt;/code&gt;, &lt;code&gt;EDIElement&lt;/code&gt;), value objects (&lt;code&gt;Standard&lt;/code&gt;, &lt;code&gt;Version&lt;/code&gt;, &lt;code&gt;MessageType&lt;/code&gt;, &lt;code&gt;Delimiters&lt;/code&gt;), and business rules.&lt;/p&gt;

&lt;p&gt;Now it's time for the &lt;strong&gt;Application Layer&lt;/strong&gt; — the part that orchestrates everything. It sits between your domain logic and the outside world:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│       Infrastructure (parsers, repos)   │
│                                         │
│  ┌───────────────────────────────────┐  │
│  │  🔥 APPLICATION LAYER             │  │  ← You are here
│  │  Use Cases · Services · Ports     │  │
│  │                                   │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │      Domain (entities)      │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The golden rule: &lt;strong&gt;the Application Layer knows the Domain, but knows nothing about Infrastructure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It talks to the outside world through &lt;strong&gt;Ports&lt;/strong&gt; — interfaces that infrastructure must implement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ports — Only Output, No Input
&lt;/h2&gt;

&lt;p&gt;In Clean Architecture, you'll often read about &lt;strong&gt;Input Ports&lt;/strong&gt; (how the outside world calls you) and &lt;strong&gt;Output Ports&lt;/strong&gt; (what you need from the outside world).&lt;/p&gt;

&lt;p&gt;In EDIFlow, we only have &lt;strong&gt;Output Ports&lt;/strong&gt;. Here's why:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input Ports&lt;/strong&gt; (Driving Ports) define how external actors invoke Use Cases. In EDIFlow, we currently have a CLI that calls Use Cases — and a REST API or web UI may come later. But we still don't need Input Port interfaces. Why? Because the &lt;strong&gt;DTO is the contract&lt;/strong&gt;. Whether the CLI, a REST controller, or a React frontend invokes the Use Case, they all do the same thing: build a &lt;code&gt;ParseEDIInputDTO&lt;/code&gt; and call &lt;code&gt;useCase.execute(dto)&lt;/code&gt;. A separate &lt;code&gt;IParseEDIUseCase&lt;/code&gt; interface wouldn't add value — the method signature is identical regardless of who calls it.&lt;/p&gt;

&lt;p&gt;And if that ever changes? Adding an Input Port interface is a 5-minute refactoring — extract an interface from the Use Case, done. The architecture supports it without any structural changes. We just don't add abstractions before they're needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output Ports&lt;/strong&gt; (Driven Ports) define what the Application Layer &lt;em&gt;needs&lt;/em&gt; from Infrastructure — and that's where the real value is:&lt;/p&gt;

&lt;h3&gt;
  
  
  IMessageParser — "Give me something that can parse"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMessageParser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ParserConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;getSupportedStandard&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;canParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ediString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;getConfiguration&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ParserConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Application Layer says: &lt;em&gt;"I need something that can parse an EDI string and return an &lt;code&gt;EDIMessage&lt;/code&gt;. I don't care if it's EDIFACT, X12, or anything else."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Implementations: &lt;code&gt;EdifactMessageParser&lt;/code&gt;, &lt;code&gt;X12MessageParser&lt;/code&gt; — both in the Infrastructure layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  IMessageBuilder — "Give me something that can build"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMessageBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;MessageBuilderOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;getSupportedStandard&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Takes a domain &lt;code&gt;EDIMessage&lt;/code&gt;, returns a raw EDI string. Format-agnostic — EDIFACT gets &lt;code&gt;'&lt;/code&gt; terminators, X12 gets &lt;code&gt;~&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  IValidationService — "Give me something that can validate"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IValidationService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ValidationContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ValidationResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generic — works with &lt;code&gt;EDIMessage&lt;/code&gt; but could validate anything. Uses Strategy + Chain of Responsibility patterns: swap validators, chain them.&lt;/p&gt;

&lt;h3&gt;
  
  
  IMessageStructureRepository — "Give me message definitions"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMessageStructureRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getMessageStructure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageStructureDTO&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;getAllMessageTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;hasMessageStructure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;getCodeList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;codeListCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the port that data packages (&lt;code&gt;@ediflow/edifact-d20b&lt;/code&gt;, &lt;code&gt;@ediflow/x12-004010&lt;/code&gt;, ...) implement. 126–319 JSON definitions per package, loaded at runtime through this single interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why only Output Ports?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Input Port (Driving)&lt;/th&gt;
&lt;th&gt;Output Port (Driven)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direction&lt;/td&gt;
&lt;td&gt;Outside → Application&lt;/td&gt;
&lt;td&gt;Application → Infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EDIFlow&lt;/td&gt;
&lt;td&gt;❌ Not needed — DTO is the contract, CLI/API/UI all call &lt;code&gt;execute(dto)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ All infrastructure contracts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Benefit&lt;/td&gt;
&lt;td&gt;Useful when entry points need different method signatures&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Essential&lt;/strong&gt; — decouples Application from Infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rule of thumb: &lt;strong&gt;if every caller uses the same &lt;code&gt;execute(dto)&lt;/code&gt; method, you don't need an Input Port — the DTO is your contract&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters for EDIFlow:&lt;/strong&gt; The EDIFACT parser, the X12 parser, the HIPAA validator — they all implement these same Output Port interfaces. The Application Layer doesn't know any of them exist. When we added X12 support months after EDIFACT, zero code changed in the Application Layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Use Cases — One Class, One User Intention
&lt;/h2&gt;

&lt;p&gt;A Use Case represents &lt;strong&gt;one thing a user wants to do&lt;/strong&gt;. Not more. In EDIFlow we have three:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;User Intention&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ParseEDIUseCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"I want to parse this EDI string"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ValidateMessageUseCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"I want to validate this message"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BuildEDIUseCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"I want to build an EDI string from data"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ParseEDIUseCase — The Real Code
&lt;/h3&gt;

&lt;p&gt;Here's what actually runs when you call &lt;code&gt;service.parse()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParseEDIUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;messageParser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IMessageParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StructureMappingService&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParseEDIInputDTO&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ParseEDIOutputDTO&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Guard: empty input&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createErrorResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EMPTY_MESSAGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Message cannot be empty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Guard: parser compatibility&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;canParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createErrorResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNSUPPORTED_FORMAT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Parser does not support this message format&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Phase 1: EDI String → EDIMessage&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parserConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Phase 2: EDIMessage → Business Object (optional)&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;businessObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returnTypedObject&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageStructure&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;businessObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageStructure&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;businessObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;segmentCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createErrorResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PARSE_ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Validate input&lt;/strong&gt; — fail fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegate to port&lt;/strong&gt; — &lt;code&gt;this.messageParser.parse()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optionally enrich&lt;/strong&gt; — Phase 2 mapping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return a DTO&lt;/strong&gt; — never expose domain internals directly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No framework. No decorators. No magic. Just TypeScript classes with constructor injection.&lt;/p&gt;




&lt;h3&gt;
  
  
  ValidateMessageUseCase — Composing Validation Phases
&lt;/h3&gt;

&lt;p&gt;Validation is more complex — it runs in &lt;strong&gt;three phases&lt;/strong&gt;, each building on the previous:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidateMessageUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;validationService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IValidationService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ValidateMessageInputDTO&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ValidateMessageOutputDTO&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Phase 1: Syntax validation&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;syntaxResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Phase 2: Structure validation (segments in correct order?)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;structureResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;performStructuralValidation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;syntaxResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Phase 3: Code list validation (valid code values?)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;performCodeValidation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;structureResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildSuccessResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part: in &lt;strong&gt;strict mode&lt;/strong&gt;, Phase 2 only runs if Phase 1 passed. Phase 3 only runs if Phase 2 passed. This prevents cascading error messages that confuse users.&lt;/p&gt;




&lt;h3&gt;
  
  
  BuildEDIUseCase — From Business Object to EDI
&lt;/h3&gt;

&lt;p&gt;Building works in reverse — you provide either an &lt;code&gt;EDIMessage&lt;/code&gt; directly, or a business object that gets unmapped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BuildEDIUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;messageBuilder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IMessageBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;envelopeBuilder&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IEnvelopeBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;structureMappingService&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StructureMappingService&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BuildEDIInputDTO&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;BuildEDIOutputDTO&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get message from either direct input or business object unmap&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessageFromInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Optionally wrap with envelope (UNB/UNH for EDIFACT, ISA/GS for X12)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includeEnvelope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrapWithEnvelope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Optionally validate before building&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateBeforeBuild&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validateMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Build the EDI string&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ediString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;OutputFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPACT&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ediString&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;strong&gt;round-trip&lt;/strong&gt; feature: &lt;code&gt;parse()&lt;/code&gt; converts EDI → JSON, &lt;code&gt;build()&lt;/code&gt; converts JSON → EDI. Same message structure definition drives both directions.&lt;/p&gt;




&lt;h2&gt;
  
  
  DTOs — Type-Safe Input/Output
&lt;/h2&gt;

&lt;p&gt;Every Use Case has a Request (Input) and a Response (Output) DTO. No domain objects leak out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ParseEDIInputDTO&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;strictMode&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;parserConfig&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ParserConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;returnTypedObject&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;messageStructure&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;MessageStructureDTO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;mappingKeyStrategy&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;MappingKeyStrategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output uses a &lt;strong&gt;discriminated union&lt;/strong&gt; — TypeScript narrows the type based on &lt;code&gt;success&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ParseEDIOutputDTO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;businessObject&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MessageType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;segmentCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParseError&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="nl"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;segmentCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you check &lt;code&gt;result.success === true&lt;/code&gt;, TypeScript knows &lt;code&gt;result.message&lt;/code&gt; exists. When it's &lt;code&gt;false&lt;/code&gt;, TypeScript knows &lt;code&gt;result.errors&lt;/code&gt; exists. No runtime checks needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Factory — Wiring Without a Framework
&lt;/h2&gt;

&lt;p&gt;How do Use Cases get their dependencies? Through a &lt;strong&gt;Factory&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UseCaseFactory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;parsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IMessageParser&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;builders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IMessageBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;validationService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IValidationService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;createParseUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StructureMappingService&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ParseEDIUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`No parser registered for standard: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ParseEDIUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;createBuildUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StructureMappingService&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;BuildEDIUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;builders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`No builder registered for standard: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BuildEDIUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;createValidateUseCase&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ValidateMessageUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidateMessageUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validationService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No DI container. No &lt;code&gt;@Injectable()&lt;/code&gt;. Just a Map of implementations and a factory method. Simple, testable, no framework coupling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing a Use Case — Easy When Dependencies Are Interfaces
&lt;/h2&gt;

&lt;p&gt;Because Use Cases depend only on interfaces, testing is trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ParseEDIUseCase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ParseEDIUseCase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@domain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParseEDIUseCase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns error for empty message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;canParse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ParseEDIUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockParser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EMPTY_MESSAGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delegates to parser and returns structured result&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;canParse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mockReturnValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mockReturnValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockMessage&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;getConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mockReturnValue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ParseEDIUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockParser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UNH+1+ORDERS:D:96A:UN'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledOnce&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No HTTP server to spin up. No database. No parsers. Just a mock that implements the interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;After building this layer, here's what I'd do the same again — and what I'd change:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Keep Use Cases stupid simple&lt;/strong&gt; — they validate input, call a port, return a DTO. If a Use Case is complex, logic belongs in a Domain Service or the Use Case is doing too much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Discriminated unions for output DTOs&lt;/strong&gt; — TypeScript's type narrowing eliminates an entire class of null-check bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Ports (interfaces) first&lt;/strong&gt; — define what you need before building what provides it. The EDIFACT parser and X12 parser were built months apart, both implementing the same &lt;code&gt;IMessageParser&lt;/code&gt;. Zero changes to the Application Layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Optional dependencies work fine&lt;/strong&gt; — &lt;code&gt;StructureMappingService&lt;/code&gt; as optional constructor parameter is genuinely optional functionality (Phase 2 mapping). Not every user needs business object mapping. Keeping it optional in the same Use Case avoids creating a second &lt;code&gt;ParseToBusinessObjectUseCase&lt;/code&gt; that would duplicate the entire parsing pipeline. Sometimes a simple &lt;code&gt;?&lt;/code&gt; is the right call.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next — Part 4: Infrastructure Layer
&lt;/h2&gt;

&lt;p&gt;In the next part, we'll show how the interfaces we defined here get implemented — the EDIFACT tokenizer, X12 envelope parser, file-based repositories, and how data packages with 126–319 message definitions per package are loaded at runtime.&lt;/p&gt;

&lt;p&gt;All of this is already built and running in production. Part 4 will walk through the real implementation code.&lt;/p&gt;

&lt;p&gt;This is where the architecture pays off: the EDIFACT parser and the X12 parser were built months apart — both implementing the same &lt;code&gt;IMessageParser&lt;/code&gt; interface. Zero changes needed in the Application Layer.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://dev.to/helloediflow/building-ediflow-a-clean-architecture-journey-in-typescript-part-1-why-1hhm"&gt;Part 1: Why Clean Architecture?&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://dev.to/helloediflow/building-ediflow-a-clean-architecture-journey-in-typescript-part-2-domain-layer-3g24"&gt;Part 2: Domain Layer&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;GitHub: @ediflow/core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ If this series is useful — a &lt;strong&gt;star on GitHub&lt;/strong&gt; helps others find it: &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;github.com/ediflow-lib/core&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What patterns do you use in your Application Layer? Controllers? Mediators? Interactors? I'd love to hear — drop a comment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>cleanarchitecture</category>
      <category>opensource</category>
      <category>edi</category>
    </item>
    <item>
      <title>EDIFlow v0.3.0: X12, HIPAA &amp; EANCOM Support for TypeScript — The Way of Water</title>
      <dc:creator>hello-ediflow</dc:creator>
      <pubDate>Mon, 04 May 2026 05:23:18 +0000</pubDate>
      <link>https://forem.com/helloediflow/ediflow-v030-x12-hipaa-eancom-support-for-typescript-the-way-of-water-gek</link>
      <guid>https://forem.com/helloediflow/ediflow-v030-x12-hipaa-eancom-support-for-typescript-the-way-of-water-gek</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Water doesn't fight obstacles. It flows."&lt;/em&gt; — Lao Tzu&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;EDI data should move through systems naturally — adapting to any standard, connecting everything, never fighting complexity. That's the philosophy behind &lt;strong&gt;EDIFlow&lt;/strong&gt;. And v0.3.0 is the biggest step yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is EDIFlow?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;EDIFlow&lt;/strong&gt; is an open-source TypeScript library for parsing, validating and building EDI messages — type-safe, zero-config, built with Clean Architecture.&lt;/p&gt;

&lt;p&gt;If you've ever dealt with raw EDI like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       *260101*1200*^*00401*000000001*0*T*:~
GS*PO*SENDER*RECEIVER*20260101*1200*1*X*004010~
ST*850*0001~
BEG*00*SA*ORDER12345**20260101~
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and wished there was a modern, type-safe TypeScript API to work with it — that's exactly what EDIFlow solves.&lt;/p&gt;




&lt;h2&gt;
  
  
  v0.3.0 — What's new
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔷 X12 — Two full versions
&lt;/h3&gt;

&lt;p&gt;X12 is the dominant EDI standard in North America — used in retail, logistics, healthcare, finance.&lt;/p&gt;

&lt;p&gt;v0.3.0 ships &lt;strong&gt;two complete X12 version packages&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Transaction Sets&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ediflow/x12-004010&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;X12 004010&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;293 transaction sets&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ediflow/x12-006040&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;X12 006040&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;319 transaction sets&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;850 (Purchase Order), 810 (Invoice), 856 (Ship Notice), 837 (Healthcare Claim), 835 (Remittance), 997 (Acknowledgment), 214, 204, 210 and hundreds more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;X12ServiceBuilder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ediflow/x12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x12_004010Repository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ediflow/x12-004010&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;X12ServiceBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x12_004010Repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawX12String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Fully typed — TypeScript knows the structure&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'ISA'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switch to 006040 by swapping one import — no other code changes needed.&lt;/p&gt;




&lt;h3&gt;
  
  
  🏥 HIPAA X12 005010 — Healthcare EDI
&lt;/h3&gt;

&lt;p&gt;14 HIPAA-specific transaction sets out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;837P&lt;/strong&gt; — Professional Claim&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;837I&lt;/strong&gt; — Institutional Claim&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;835&lt;/strong&gt; — Remittance Advice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;270/271&lt;/strong&gt; — Eligibility Inquiry &amp;amp; Response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;276/277&lt;/strong&gt; — Claim Status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;834&lt;/strong&gt; — Benefit Enrollment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;820&lt;/strong&gt; — Payment Order
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/x12 @ediflow/hipaa-x12-005010
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🌍 EDIFACT — Full coverage across 4 versions
&lt;/h3&gt;

&lt;p&gt;EDIFACT is the international standard — dominant in Europe, logistics, supply chain.&lt;/p&gt;

&lt;p&gt;All four versions are supported with &lt;strong&gt;complete message sets&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Messages&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ediflow/edifact-d96a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;D.96A&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;126 message types&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ediflow/edifact-d01b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;D.01B&lt;/td&gt;
&lt;td&gt;full standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ediflow/edifact-d12a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;D.12A&lt;/td&gt;
&lt;td&gt;full standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ediflow/edifact-d20b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;D.20B&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;195 message types&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every message type in the standard — ORDERS, INVOIC, DESADV, RECADV, SLSRPT, PRICAT, REMADV, IFTMIN, COPARN, APERAK, CONTRL, and hundreds more — available out of the box. No custom definitions needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/edifact @ediflow/edifact-d20b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🛒 EANCOM 2002 — GS1 Retail Standard
&lt;/h3&gt;

&lt;p&gt;50 GS1 retail message types built on EDIFACT syntax. Covers the complete retail supply chain with full GS1 GLN party identification and EAN-13/GTIN product codes:&lt;/p&gt;

&lt;p&gt;ORDERS, ORDRSP, DESADV, RECADV, INVOIC, REMADV, SLSRPT, PRICAT, INVRPT, PRODAT and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/edifact @ediflow/eancom-2002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔄 Business Object Mapping — Round-Trip
&lt;/h3&gt;

&lt;p&gt;This is the feature I'm most excited about. Instead of manually navigating segment arrays, you map EDI messages directly to/from typed JSON objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StructureMappingService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ediflow/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mappingService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StructureMappingService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// EDI → Business Object (JSON)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mappingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsedMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BGM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1004&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// "ORDER12345" — document number&lt;/span&gt;

&lt;span class="c1"&gt;// Modify the business object&lt;/span&gt;
&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BGM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1004&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ORDER99999&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Business Object → EDI (rebuild round-trip)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rebuilt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mappingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five key strategies for property names: &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;camelCase&lt;/code&gt;, &lt;code&gt;snake_case&lt;/code&gt;, &lt;code&gt;kebab-case&lt;/code&gt; — choose what fits your codebase.&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚙️ CLI enhancements
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@ediflow/cli&lt;/code&gt; package now supports business object output and schema export for both EDIFACT and X12:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Parse to business object&lt;/span&gt;
npx @ediflow/cli parse invoice.edi &lt;span class="nt"&gt;--output-type&lt;/span&gt; business-object

&lt;span class="c"&gt;# Export full JSON schema for a message type&lt;/span&gt;
npx @ediflow/cli export-schema &lt;span class="nt"&gt;--standard&lt;/span&gt; x12 &lt;span class="nt"&gt;--version&lt;/span&gt; 004010 &lt;span class="nt"&gt;--message&lt;/span&gt; 850

&lt;span class="c"&gt;# Build EDI from a JSON business object&lt;/span&gt;
npx @ediflow/cli build order.json &lt;span class="nt"&gt;--standard&lt;/span&gt; edifact &lt;span class="nt"&gt;--version&lt;/span&gt; d20b &lt;span class="nt"&gt;--message&lt;/span&gt; ORDERS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📂 Ready-to-run examples
&lt;/h2&gt;

&lt;p&gt;Clone the repo and run any example with &lt;code&gt;npx tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ediflow-lib/core.git
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# X12&lt;/span&gt;
npx tsx examples/x12/parse-850/index.ts        &lt;span class="c"&gt;# Parse Purchase Order&lt;/span&gt;
npx tsx examples/x12/build-850/index.ts        &lt;span class="c"&gt;# Build 850 + round-trip&lt;/span&gt;
npx tsx examples/x12/validate-837/index.ts     &lt;span class="c"&gt;# Validate Healthcare Claim&lt;/span&gt;
npx tsx examples/x12/multi-message/index.ts    &lt;span class="c"&gt;# Parse 850 + 810 + 856 + 997&lt;/span&gt;

&lt;span class="c"&gt;# EDIFACT&lt;/span&gt;
npx tsx examples/edifact/parse-orders/index.ts    &lt;span class="c"&gt;# Parse ORDERS&lt;/span&gt;
npx tsx examples/edifact/build-invoic/index.ts    &lt;span class="c"&gt;# Build INVOIC + round-trip&lt;/span&gt;

&lt;span class="c"&gt;# HIPAA&lt;/span&gt;
npx tsx examples/hipaa/parse-837/index.ts     &lt;span class="c"&gt;# Parse 837P Professional Claim&lt;/span&gt;

&lt;span class="c"&gt;# EANCOM&lt;/span&gt;
npx tsx examples/eancom/parse-desadv/index.ts &lt;span class="c"&gt;# Parse DESADV (GLN + EAN-13)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All examples include real EDI sample data and commented output — no setup beyond &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  By the numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📦 13 packages on NPM&lt;/li&gt;
&lt;li&gt;🔷 X12: 293 + 319 transaction sets across 2 versions&lt;/li&gt;
&lt;li&gt;🌍 EDIFACT: 126 + 195 message types across 4 versions&lt;/li&gt;
&lt;li&gt;🛒 EANCOM: 50 GS1 retail messages&lt;/li&gt;
&lt;li&gt;🏥 HIPAA: 14 transaction sets&lt;/li&gt;
&lt;li&gt;✅ 711 tests passing&lt;/li&gt;
&lt;li&gt;📊 ≥ 90% code coverage (&lt;code&gt;@ediflow/core&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;0 TypeScript errors, 0 warnings&lt;/li&gt;
&lt;li&gt;⚖️ MIT License&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# X12 004010 (293 transaction sets)&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/x12 @ediflow/x12-004010

&lt;span class="c"&gt;# X12 006040 (319 transaction sets)&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/x12 @ediflow/x12-006040

&lt;span class="c"&gt;# EDIFACT D.20B (195 messages)&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/edifact @ediflow/edifact-d20b

&lt;span class="c"&gt;# HIPAA&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/x12 @ediflow/hipaa-x12-005010

&lt;span class="c"&gt;# EANCOM 2002&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core @ediflow/edifact @ediflow/eancom-2002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ &lt;a href="https://github.com/ediflow-lib/core/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;Full changelog&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://github.com/ediflow-lib/core/tree/main/examples" rel="noopener noreferrer"&gt;Examples&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://github.com/ediflow-lib/core/blob/main/docs/QUICK_START.md" rel="noopener noreferrer"&gt;Quick Start&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Way of Water 🌊
&lt;/h2&gt;

&lt;p&gt;I've been building EDIFlow alone — no team, no funding. Just a belief that TypeScript developers working in supply chain, logistics, and healthcare deserve better EDI tooling than what exists today.&lt;/p&gt;

&lt;p&gt;The name isn't accidental. Inspired by the Taoist "Way of Water": EDI data should move through systems naturally — adapting to any standard, connecting everything, never forcing complexity on the developer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💧 Finds its way&lt;/strong&gt; — intelligent parsing, adaptive error handling&lt;br&gt;
&lt;strong&gt;🌊 Adapts to any form&lt;/strong&gt; — EDIFACT, X12, HIPAA, EANCOM, more coming&lt;br&gt;
&lt;strong&gt;💪 Powerful but gentle&lt;/strong&gt; — enterprise-grade performance, clean API&lt;br&gt;
&lt;strong&gt;🔄 Connects everything&lt;/strong&gt; — one unified interface for all EDI standards&lt;/p&gt;

&lt;p&gt;If this resonates — or if you're tired of fighting brittle EDI parsers — consider giving the project a ⭐ on GitHub. It helps others find it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;⭐ Star EDIFlow on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What EDI standard are you working with? Drop a comment — I'd love to know what matters most for the next release.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Building EDIFlow in TypeScript: Part 2 - Domain Layer Deep-Dive</title>
      <dc:creator>hello-ediflow</dc:creator>
      <pubDate>Tue, 10 Feb 2026 11:08:44 +0000</pubDate>
      <link>https://forem.com/helloediflow/building-ediflow-domain-layer-deep-dive-en0</link>
      <guid>https://forem.com/helloediflow/building-ediflow-domain-layer-deep-dive-en0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Series:&lt;/strong&gt; Building EDIFlow - A Clean Architecture Journey in TypeScript (Part 2/6)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; EDIFlow&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Published:&lt;/strong&gt; February 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading Time:&lt;/strong&gt; ~10-12 minutes  &lt;/p&gt;


&lt;h2&gt;
  
  
  Welcome Back! 👋
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/helloediflow/building-ediflow-in-typescript-part-1-why-clean-architecture-1hhm"&gt;Part 1&lt;/a&gt;, I shared &lt;strong&gt;why&lt;/strong&gt; I built EDIFlow and why Clean Architecture was the right choice.&lt;/p&gt;

&lt;p&gt;Now it’s time for the &lt;strong&gt;hard part&lt;/strong&gt;: &lt;strong&gt;design decisions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Today we focus on the &lt;strong&gt;Domain Layer&lt;/strong&gt; — not the implementation details, but the &lt;strong&gt;decisions&lt;/strong&gt; that shaped it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you’ll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What objects&lt;/strong&gt; to model (and what NOT to model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision criteria&lt;/strong&gt; for Entity vs Value Object&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trade-offs&lt;/strong&gt; that influenced the design&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why DDD + Clean Architecture belong together&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you can reuse&lt;/strong&gt; in your own systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No perfect solutions — just real decisions with real trade-offs.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. The First Question: What IS the Domain? 🤔
&lt;/h2&gt;

&lt;p&gt;This sounds obvious, but it’s the &lt;strong&gt;hardest question&lt;/strong&gt; in Clean Architecture:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“What belongs in the Domain Layer?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uncle Bob says in &lt;a href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164" rel="noopener noreferrer"&gt;Clean Architecture&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“The Domain Layer contains Enterprise Business Rules. These are the rules that would exist even if the application didn't exist.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Translation:&lt;/strong&gt; Domain = &lt;strong&gt;pure business logic&lt;/strong&gt;, independent of technology.&lt;/p&gt;
&lt;h3&gt;
  
  
  My Challenge with EDIFlow
&lt;/h3&gt;

&lt;p&gt;EDIFlow parses messages. But what’s the “domain”?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First instinct (wrong):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Domain = Parsing logic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❌ Parsing is technical, not business.&lt;/p&gt;

&lt;p&gt;I spent a week building parsers in the domain layer. Big mistake! When I tried to add X12 support, I had to change the domain. That's when I realized: &lt;strong&gt;if adding a new format changes your domain, your domain is wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second try:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Domain = Message structure (segments, elements)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Better, but still technical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final answer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Domain = Business concepts that exist independently
- A message has a type
- A message has a version
- A message has validation rules
- A message has identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;This is the domain.&lt;/strong&gt; It exists before parsing, after parsing, and independent of any parser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key lesson:&lt;/strong&gt; Don't confuse &lt;strong&gt;technical implementation&lt;/strong&gt; with &lt;strong&gt;business concepts&lt;/strong&gt;. I learned this the hard way!&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Enter Domain-Driven Design 📚
&lt;/h2&gt;

&lt;p&gt;Now I knew &lt;strong&gt;what&lt;/strong&gt; belongs in the Domain Layer. But I still had a problem: &lt;strong&gt;Which objects do I create?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clean Architecture tells me &lt;em&gt;where&lt;/em&gt; logic belongs. It doesn’t tell me &lt;strong&gt;how to model&lt;/strong&gt; that logic.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;Domain-Driven Design (DDD)&lt;/strong&gt; helps. Eric Evans describes several &lt;strong&gt;building blocks&lt;/strong&gt; for domain modeling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entities&lt;/strong&gt; - Objects with identity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value Objects&lt;/strong&gt; - Objects defined by value&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggregates&lt;/strong&gt; - Cluster of entities with a root&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Services&lt;/strong&gt; - Operations that don’t belong to objects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repositories&lt;/strong&gt; - Persistence abstractions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Factories&lt;/strong&gt; - Complex object creation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These ideas existed before DDD, but Evans made them &lt;strong&gt;clear and practical&lt;/strong&gt; for domain modeling.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Why DDD + Clean Architecture? 🔗
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why do I need both?&lt;/strong&gt; Because they solve &lt;strong&gt;different problems&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Domain-Driven Design&lt;/th&gt;
&lt;th&gt;Clean Architecture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Answers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;WHAT&lt;/strong&gt; to model&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;HOW&lt;/strong&gt; to structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Question&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What objects? What relationships?&lt;/td&gt;
&lt;td&gt;Where does code go?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gives you&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Entities, Value Objects, Aggregates&lt;/td&gt;
&lt;td&gt;Layers, Dependencies, Boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model business correctly&lt;/td&gt;
&lt;td&gt;Structure code maintainably&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Together they’re powerful:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DDD tells you WHAT:
- EDIMessage is an Entity (identity)
- Version is a Value Object (value-based)
- Standard is an Enum (type-safe set)

Clean Architecture tells you WHERE:
- Entities live in Domain Layer
- Parsers live in Infrastructure Layer
- Use Cases live in Application Layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real example (compact):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DDD Decision:
"Version is a Value Object" (immutable, no identity)

Clean Architecture Decision:
"Version lives in domain/value-objects/ with zero dependencies"

Result:
✅ Correct model (DDD)
✅ Correct structure (Clean Architecture)
✅ Portable + testable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Both complement each other:&lt;/strong&gt; DDD without structure = mess. Structure without domain insight = technical code without business value.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why Focus Only on Entities &amp;amp; Value Objects? 🎯
&lt;/h2&gt;

&lt;p&gt;DDD has many building blocks, but &lt;strong&gt;this article focuses on just two&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Entities&lt;/strong&gt; - objects with identity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value Objects&lt;/strong&gt; - objects defined by value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why only these two?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest answer:&lt;/strong&gt; Because EDIFlow’s Domain Layer is &lt;strong&gt;simple&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ No Aggregates (EDIMessage is simple enough)&lt;/li&gt;
&lt;li&gt;✅ No Domain Services (operations fit in objects or use cases)&lt;/li&gt;
&lt;li&gt;✅ No Repositories in Domain (they belong in Infrastructure)&lt;/li&gt;
&lt;li&gt;✅ No Factories needed (constructors are enough)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;YAGNI applies here.&lt;/strong&gt; Don’t add patterns you don’t need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Later parts in the series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 3:&lt;/strong&gt; Use Cases, Services, DTOs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4:&lt;/strong&gt; Repositories, Parsers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. The Big Decision: Entity or Value Object? 📦
&lt;/h2&gt;

&lt;p&gt;I use a simple decision matrix:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;If YES →&lt;/th&gt;
&lt;th&gt;If NO →&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Does it have an ID?&lt;/td&gt;
&lt;td&gt;Entity&lt;/td&gt;
&lt;td&gt;Value Object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can two be equal with different values?&lt;/td&gt;
&lt;td&gt;Entity&lt;/td&gt;
&lt;td&gt;Value Object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does it change over time?&lt;/td&gt;
&lt;td&gt;Entity&lt;/td&gt;
&lt;td&gt;Value Object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Do I need to track its history?&lt;/td&gt;
&lt;td&gt;Entity&lt;/td&gt;
&lt;td&gt;Value Object&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Applied to all EDIFlow domain objects:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object&lt;/th&gt;
&lt;th&gt;Has ID?&lt;/th&gt;
&lt;th&gt;Equal w/ diff values?&lt;/th&gt;
&lt;th&gt;Changes?&lt;/th&gt;
&lt;th&gt;Track history?&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EDIMessage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Entity&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Value Object&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MessageType&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Value Object&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delimiters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Value Object&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Value Object (Enum)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EDISegment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Value Object&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EDIElement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Value Object&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Only &lt;code&gt;EDIMessage&lt;/code&gt; has identity. Everything else is a &lt;strong&gt;Value Object&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight (1:6 ratio):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1 Entity&lt;/strong&gt; (EDIMessage)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6 Value Objects&lt;/strong&gt; (everything else)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DDD takeaway:&lt;/strong&gt; Start by assuming Value Object. Only choose Entity if identity truly matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  See It In Action
&lt;/h3&gt;

&lt;p&gt;Here's how the decision matrix plays out in real code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Entity: Has identity, can be equal despite different values&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EDIMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ORDER-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EDIMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ORDER-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;message1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ✅ true (same ID!)&lt;/span&gt;

&lt;span class="c1"&gt;// Value Object: No identity, equals by value only&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D.96A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D.96A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ✅ true (same value!)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D.01B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ❌ false (different value!)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Two orders with same content are &lt;strong&gt;different orders&lt;/strong&gt; (Entity). Two versions "D.96A" are the &lt;strong&gt;same version&lt;/strong&gt; (Value Object).&lt;/p&gt;




&lt;h2&gt;
  
  
  6. The Hardest Decision: Polymorphism vs Simplicity 🎭
&lt;/h2&gt;

&lt;p&gt;EDIFlow supports multiple formats (EDIFACT, X12, IDOC). Each has &lt;strong&gt;different rules&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I model this?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I struggled with this for &lt;strong&gt;days&lt;/strong&gt;. Every approach felt wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate classes = duplication&lt;/li&gt;
&lt;li&gt;if/else = messy&lt;/li&gt;
&lt;li&gt;Polymorphism = complex?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me show you the three options I considered:&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Separate Classes (Naive)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactVersion&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;X12Version&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IdocVersion&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Simple, clear&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Duplication, hard to extend&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Rejected&lt;/strong&gt; (violates DRY)&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 2: if/else Everywhere
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standard&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EDIFACT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; One class&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Breaks OCP, hard to test&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Rejected&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 3: Polymorphic Value Object (Chosen)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.../&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;X12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.../&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; One class, extensible, type-safe&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Slightly more complex&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Chosen&lt;/strong&gt; (best trade-off)&lt;/p&gt;


&lt;h2&gt;
  
  
  7. Trade-Off Example: Validation Strategy 📊
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;th&gt;Chosen?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;External Validator&lt;/td&gt;
&lt;td&gt;Flexible&lt;/td&gt;
&lt;td&gt;Separate logic&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-validating constructor&lt;/td&gt;
&lt;td&gt;Fail-fast&lt;/td&gt;
&lt;td&gt;Exceptions&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Factory method&lt;/td&gt;
&lt;td&gt;Controlled&lt;/td&gt;
&lt;td&gt;Extra layer&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why self-validation:&lt;/strong&gt; It makes invalid state impossible.&lt;/p&gt;


&lt;h2&gt;
  
  
  8. What I'd Do Differently 🤔
&lt;/h2&gt;

&lt;p&gt;Let me be honest about &lt;strong&gt;mistakes and second thoughts&lt;/strong&gt;:&lt;/p&gt;
&lt;h3&gt;
  
  
  Mistake #1: Almost Over-Engineered Version
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What I almost did:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IVersion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactVersion&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IVersion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;X12Version&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IVersion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why I didn't:&lt;/strong&gt; YAGNI! The polymorphic approach works great. Don't create interfaces until you &lt;strong&gt;need&lt;/strong&gt; them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Resist the urge to make everything "extensible." Simple solutions often stay simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake #2: First Draft Had 50+ Classes
&lt;/h3&gt;

&lt;p&gt;My &lt;strong&gt;very first&lt;/strong&gt; attempt had separate classes for everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EdifactDelimiters&lt;/code&gt;, &lt;code&gt;X12Delimiters&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EdifactSegment&lt;/code&gt;, &lt;code&gt;X12Segment&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EdifactElement&lt;/code&gt;, &lt;code&gt;X12Element&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why that was bad:&lt;/strong&gt; Massive duplication. 80% of the code was identical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; Look for patterns &lt;strong&gt;before&lt;/strong&gt; creating classes. Most Value Objects are format-agnostic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thought #1: Should I Use Result Types?
&lt;/h3&gt;

&lt;p&gt;Right now, constructors throw exceptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EDIFACT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INVALID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// throws!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternative:&lt;/strong&gt; Result types (functional style):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INVALID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// returns Result&amp;lt;Version, Error&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why I didn't:&lt;/strong&gt; Most TypeScript developers expect exceptions. Result types are great, but add learning curve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Would I change it?&lt;/strong&gt; Maybe in v2.0 with a &lt;code&gt;Version.tryCreate()&lt;/code&gt; method. For now, exceptions work fine.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. What You Get From This Design 🎁
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Type safety&lt;/strong&gt; (less guessing)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Invalid state impossible&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Clear separation of concerns&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Easier testing&lt;/strong&gt; (pure logic)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;No breaking changes&lt;/strong&gt; when adding formats&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. What's Next? 🚀
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; Application Layer&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Cases&lt;/li&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;li&gt;DTOs&lt;/li&gt;
&lt;li&gt;Dependency Injection (no framework)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  11. Conclusion 🎯
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Domain design is about decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What to model&lt;/li&gt;
&lt;li&gt;How to classify objects&lt;/li&gt;
&lt;li&gt;What trade-offs to accept&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EDIFlow’s Domain Model:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1 Entity:&lt;/strong&gt; &lt;code&gt;EDIMessage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6 Value Objects:&lt;/strong&gt; &lt;code&gt;Standard&lt;/code&gt;, &lt;code&gt;Version&lt;/code&gt;, &lt;code&gt;MessageType&lt;/code&gt;, &lt;code&gt;Delimiters&lt;/code&gt;, &lt;code&gt;EDISegment&lt;/code&gt;, &lt;code&gt;EDIElement&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; Start with Value Object. Promote to Entity only when identity truly matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Series Navigation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 1 (Published):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/helloediflow/building-ediflow-in-typescript-part-1-why-clean-architecture-1hhm"&gt;Part 1: Why Clean Architecture?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Upcoming Parts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 3: Application Layer - Use Cases &amp;amp; Services&lt;/li&gt;
&lt;li&gt;Part 4: Infrastructure Layer &amp;amp; Monorepo&lt;/li&gt;
&lt;li&gt;Part 5: Presentation Layer - CLI&lt;/li&gt;
&lt;li&gt;Part 6: Lessons Learned &amp;amp; The Big Refactoring&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 Let’s Discuss
&lt;/h2&gt;

&lt;p&gt;How do you decide between &lt;strong&gt;Entity&lt;/strong&gt; and &lt;strong&gt;Value Object&lt;/strong&gt; in your projects?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you prefer decision matrices like this?&lt;/li&gt;
&lt;li&gt;What trade-offs do you value most (simplicity, extensibility, strictness)?&lt;/li&gt;
&lt;li&gt;Have you used DDD + Clean Architecture together?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;I’d love to hear your approach — drop your thoughts in the comments.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🔗 Links &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;EDIFlow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://www.npmjs.com/package/@ediflow/core" rel="noopener noreferrer"&gt;npm: @ediflow/core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;GitHub: ediflow-lib/core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;a href="https://github.com/ediflow-lib/core/blob/main/docs/ARCHITECTURE.md" rel="noopener noreferrer"&gt;Architecture Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Community:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💬 &lt;a href="https://discord.gg/S5npqcv8Zu" rel="noopener noreferrer"&gt;Discord: EDIFlow Community&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📣 &lt;a href="https://github.com/ediflow-lib/core/discussions" rel="noopener noreferrer"&gt;GitHub Discussions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Books:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📚 &lt;a href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164" rel="noopener noreferrer"&gt;Clean Architecture by Robert C. Martin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215" rel="noopener noreferrer"&gt;Domain-Driven Design by Eric Evans&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882" rel="noopener noreferrer"&gt;Clean Code by Robert C. Martin&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Part 3 is next: Application Layer.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>cleanarchitecture</category>
      <category>edifact</category>
      <category>x12</category>
    </item>
    <item>
      <title>Building EDIFlow in TypeScript: Part 1 - Why Clean Architecture?</title>
      <dc:creator>hello-ediflow</dc:creator>
      <pubDate>Tue, 03 Feb 2026 06:14:36 +0000</pubDate>
      <link>https://forem.com/helloediflow/building-ediflow-in-typescript-part-1-why-clean-architecture-1hhm</link>
      <guid>https://forem.com/helloediflow/building-ediflow-in-typescript-part-1-why-clean-architecture-1hhm</guid>
      <description>&lt;p&gt;&lt;strong&gt;Series:&lt;/strong&gt; Building EDIFlow - A Clean Architecture Journey in TypeScript (Part 1/6)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; EDIFlow&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Published:&lt;/strong&gt; February 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading Time:&lt;/strong&gt; ~7 minutes  &lt;/p&gt;


&lt;h2&gt;
  
  
  1. Introduction - The Problem 🤔
&lt;/h2&gt;

&lt;p&gt;I've been working in data integration for over 20 years. SOAP, REST APIs, and yes—&lt;strong&gt;EDI&lt;/strong&gt;. Lots of EDI.&lt;/p&gt;

&lt;p&gt;If you've ever worked with EDIFACT, you know the pain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNH+1+ORDERS:D:96A:UN'
BGM+220+ORDER12345+9'
DTM+137:20240201:102'
NAD+BY+BUYER123::92'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What does &lt;code&gt;BGM+220&lt;/code&gt; mean?&lt;/strong&gt; What is &lt;code&gt;DTM+137&lt;/code&gt;? What's &lt;code&gt;::92&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;"old school EDI" world&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ No human-readable names&lt;/li&gt;
&lt;li&gt;❌ Just codes and tags (BGM, NAD, DTM...)&lt;/li&gt;
&lt;li&gt;❌ Makes onboarding new developers &lt;strong&gt;incredibly hard&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the existing parsers? They don't help much:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Typical EDI Parser - Generic segments&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bgm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BGM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bgm&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// What is element[1]? 🤷&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No type safety. No IntelliSense. No business logic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After 20+ years of this, I had enough. I wanted to build an EDI library that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Maps to &lt;strong&gt;type-safe TypeScript business objects&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Uses &lt;strong&gt;Clean Architecture&lt;/strong&gt; principles&lt;/li&gt;
&lt;li&gt;✅ Makes EDI development &lt;strong&gt;enjoyable&lt;/strong&gt; (yes, really!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I started &lt;strong&gt;EDIFlow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But here's the honest truth: &lt;strong&gt;I didn't just want to build a library. I wanted to LEARN Clean Architecture by doing.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Why Clean Architecture? 📚
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Honest Answer: I Wanted to Learn It!
&lt;/h3&gt;

&lt;p&gt;I first encountered &lt;strong&gt;Clean Architecture&lt;/strong&gt; around 2020. Read the books:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882" rel="noopener noreferrer"&gt;Clean Code by Robert C. Martin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164" rel="noopener noreferrer"&gt;Clean Architecture by Robert C. Martin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445" rel="noopener noreferrer"&gt;Agile Software Development by Robert C. Martin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was &lt;strong&gt;hooked&lt;/strong&gt;. The principles made so much sense:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single Responsibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency Inversion&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testability&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But then I tried to apply it at work...&lt;/p&gt;

&lt;h3&gt;
  
  
  The Reality: Resistance to Change
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Typical problems I faced:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏢 "We've always done it this way"&lt;/li&gt;
&lt;li&gt;😰 "New architecture? Too risky!"&lt;/li&gt;
&lt;li&gt;⏰ "We don't have time to refactor"&lt;/li&gt;
&lt;li&gt;🤷 "Why change if it works?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The structures were &lt;strong&gt;too entrenched&lt;/strong&gt;. No trust in "new" architectures. The typical corporate fears.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Build My Own Project
&lt;/h3&gt;

&lt;p&gt;I realized: &lt;strong&gt;The best way to learn Clean Architecture is to build something real from scratch.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a TODO app. Not a toy example. A &lt;strong&gt;real-world library&lt;/strong&gt; that solves a &lt;strong&gt;real problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So when I decided to build EDIFlow, I made a commitment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Clean Architecture&lt;/strong&gt; from Day 1&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;TDD&lt;/strong&gt; (Test-Driven Development)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Agile&lt;/strong&gt; principles (even solo!)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;No compromises&lt;/strong&gt; on code quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Could I have hacked it together in a weekend?&lt;/strong&gt; Sure!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But where's the learning in that?&lt;/strong&gt; 🤷&lt;/p&gt;

&lt;h3&gt;
  
  
  So... Was Clean Architecture Worth It?
&lt;/h3&gt;

&lt;p&gt;This is the question everyone asks: &lt;strong&gt;"Is Clean Architecture worth the extra effort?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My answer:&lt;/strong&gt; YES! But it wasn't without challenges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What went well:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;599 tests&lt;/strong&gt; - Writing tests became easy (and fun!)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Zero coupling&lt;/strong&gt; - Changed parser implementation twice, no problem&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Added 4 EDIFACT versions&lt;/strong&gt; - Clean separation made it trivial&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Confidence&lt;/strong&gt; - I can refactor without fear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But there were challenges too:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚠️ &lt;strong&gt;More files&lt;/strong&gt; - Sometimes felt like overkill for simple features&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Initial velocity&lt;/strong&gt; - First weeks were slower ("I could have hacked this in a weekend!")&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;TypeScript DI complexity&lt;/strong&gt; - No framework = manual wiring gets verbose&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Solo discipline&lt;/strong&gt; - Easy to cut corners when no one is watching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The verdict?&lt;/strong&gt; For a library like EDIFlow: &lt;strong&gt;Absolutely worth it!&lt;/strong&gt; Every hour spent on architecture saved me days in maintenance.&lt;/p&gt;

&lt;p&gt;For a throwaway script? Probably overkill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(We'll dive into the honest pros &amp;amp; cons, with real examples, in Part 5!)&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Why TypeScript? 💙
&lt;/h2&gt;

&lt;p&gt;EDI parsing is &lt;strong&gt;complex&lt;/strong&gt;. You're dealing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;618+ message types (ORDERS, INVOIC, DESADV...)&lt;/li&gt;
&lt;li&gt;Thousands of segment definitions&lt;/li&gt;
&lt;li&gt;Complex validation rules&lt;/li&gt;
&lt;li&gt;Delimiters, escape characters, special cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Without type safety, this becomes a nightmare.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TypeScript Benefits for EDIFlow:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Type Safety for Complex Parsing Logic
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ JavaScript - Runtime errors waiting to happen&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Oops! No error! 💥&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ TypeScript - Caught at compile time&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Delimiters&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Delimiters must be different!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. IntelliSense = Developer Happiness
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Auto-completion works!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapToBusinessObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// IntelliSense suggests fields&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buyer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// Type-safe navigation&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// No guessing!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Interface-Driven Design (Perfect for Clean Architecture!)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Domain Layer - Just an interface, no implementation details&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IEDIParser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;EDIMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Infrastructure Layer - Implements the interface&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EdifactMessageParser&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IEDIParser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;EDIMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// EDIFACT-specific logic here&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;Dependency Inversion&lt;/strong&gt; in action! The domain doesn't know about EDIFACT. The domain just knows "I need a parser."&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Easy to Publish to npm
&lt;/h4&gt;

&lt;p&gt;TypeScript compiles to JavaScript, so it works everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ediflow/core     &lt;span class="c"&gt;# Works in Node.js&lt;/span&gt;
import &lt;span class="o"&gt;{&lt;/span&gt; EdifactParser &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'@ediflow/core'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;# Works in browsers (with bundler)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus, TypeScript generates &lt;code&gt;.d.ts&lt;/code&gt; files automatically—users get IntelliSense out of the box!&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Strong Ecosystem
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vitest&lt;/strong&gt; for testing (fast, great DX)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tsup&lt;/strong&gt; for building (zero config!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript strict mode&lt;/strong&gt; catches bugs early&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TypeScript Made EDI Development Enjoyable
&lt;/h3&gt;

&lt;p&gt;Before EDIFlow, EDI development was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;😫 Debugging runtime errors&lt;/li&gt;
&lt;li&gt;🤷 Guessing what &lt;code&gt;element[3]&lt;/code&gt; means&lt;/li&gt;
&lt;li&gt;💥 Breaking changes at runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With TypeScript:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Compile-time safety&lt;/li&gt;
&lt;li&gt;✅ IntelliSense everywhere&lt;/li&gt;
&lt;li&gt;✅ Refactoring with confidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TypeScript isn't just a nice-to-have for EDIFlow. It's essential.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why Open Source? 🌍
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Learning in Public
&lt;/h3&gt;

&lt;p&gt;I could have built EDIFlow as a private project. But I chose &lt;strong&gt;open source&lt;/strong&gt; for several reasons:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Accountability
&lt;/h4&gt;

&lt;p&gt;When you know others might read your code, you write &lt;strong&gt;better code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No shortcuts. No "I'll fix this later." Everything has to be &lt;strong&gt;production-ready&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Community Feedback
&lt;/h4&gt;

&lt;p&gt;Open source means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐛 Bug reports from real users&lt;/li&gt;
&lt;li&gt;💡 Feature suggestions I never thought of&lt;/li&gt;
&lt;li&gt;🤝 Potential contributors&lt;/li&gt;
&lt;li&gt;📖 Documentation improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The best products are built WITH the community, not just FOR them.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Portfolio &amp;amp; Learning
&lt;/h4&gt;

&lt;p&gt;Let's be honest: If I'm spending &lt;strong&gt;100+ hours&lt;/strong&gt; building this, I want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Show it in my portfolio&lt;/li&gt;
&lt;li&gt;✅ Get feedback from experts&lt;/li&gt;
&lt;li&gt;✅ Help others with the same problem&lt;/li&gt;
&lt;li&gt;✅ Build my personal brand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Open source is the ultimate resume.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Solving a Real Problem
&lt;/h4&gt;

&lt;p&gt;I'm not the only one struggling with EDI libraries. By open-sourcing EDIFlow, I hope to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Help developers new to EDI&lt;/li&gt;
&lt;li&gt;Provide a &lt;strong&gt;modern alternative&lt;/strong&gt; to legacy tools&lt;/li&gt;
&lt;li&gt;Show that &lt;strong&gt;EDI can be developer-friendly&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. MIT License = Maximum Freedom
&lt;/h4&gt;

&lt;p&gt;EDIFlow is &lt;strong&gt;MIT licensed&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Use it commercially&lt;/li&gt;
&lt;li&gt;✅ Modify it however you want&lt;/li&gt;
&lt;li&gt;✅ No attribution required (but appreciated!)&lt;/li&gt;
&lt;li&gt;✅ No strings attached&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why MIT?&lt;/strong&gt; Because I want &lt;strong&gt;maximum adoption&lt;/strong&gt;. The more people use it, the better it gets.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. What is EDIFlow? 🚀
&lt;/h2&gt;

&lt;p&gt;Okay, enough about motivation. &lt;strong&gt;What IS EDIFlow?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  EDI = Electronic Data Interchange
&lt;/h3&gt;

&lt;p&gt;Companies exchange business documents electronically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purchase Orders&lt;/strong&gt; (ORDERS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invoices&lt;/strong&gt; (INVOIC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shipping Notices&lt;/strong&gt; (DESADV)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory Reports&lt;/strong&gt; (INVRPT)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of paper, they send structured messages like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNH+1+ORDERS:D:96A:UN'
BGM+220+ORDER12345+9'
DTM+137:20240201:102'
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  EDIFACT = UN/EDIFACT Standard
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;EDIFACT&lt;/strong&gt; (Electronic Data Interchange For Administration, Commerce and Transport) is the &lt;strong&gt;United Nations standard&lt;/strong&gt; for EDI.&lt;/p&gt;

&lt;p&gt;It's used globally, especially in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚢 Logistics &amp;amp; Supply Chain&lt;/li&gt;
&lt;li&gt;🏭 Manufacturing&lt;/li&gt;
&lt;li&gt;🏥 Healthcare&lt;/li&gt;
&lt;li&gt;🏛️ Government&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  EDIFlow = Modern TypeScript Library for EDI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;EDIFlow is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Parser&lt;/strong&gt; - EDIFACT string → TypeScript objects&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Validator&lt;/strong&gt; - 3-phase validation (Syntax → Structure → Business)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Builder&lt;/strong&gt; - TypeScript objects → EDIFACT string&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Mapper&lt;/strong&gt; - Auto-convert to/from business objects&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Schema Exporter&lt;/strong&gt; - Generate JSON Schema &amp;amp; TypeScript types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Built with:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean Architecture (Uncle Bob)&lt;/li&gt;
&lt;li&gt;TypeScript (strict mode)&lt;/li&gt;
&lt;li&gt;TDD (599 tests!)&lt;/li&gt;
&lt;li&gt;Zero dependencies (core package)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;And this is just the beginning!&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;Right now, EDIFlow supports &lt;strong&gt;EDIFACT&lt;/strong&gt; (4 versions). But the journey continues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔜 &lt;strong&gt;X12&lt;/strong&gt; (ANSI ASC X12) - Coming soon!&lt;/li&gt;
&lt;li&gt;🔜 &lt;strong&gt;More EDI formats&lt;/strong&gt; - We're agile, we follow the flow!&lt;/li&gt;
&lt;li&gt;🔜 &lt;strong&gt;Community-driven features&lt;/strong&gt; - What does the community need?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Clean Architecture foundation makes adding new formats &lt;strong&gt;easy&lt;/strong&gt;. That's the power of good design!&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DIContainer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ediflow/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Parse EDIFACT string&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DIContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseUseCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParseEDIUseCase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parseUseCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edifactString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EDIFACT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "ORDERS"&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Map to business object (type-safe!)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BusinessObjectMapper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapToBusinessObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;         &lt;span class="c1"&gt;// "ORDER12345"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buyer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// "BUYER123"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// IntelliSense works!&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Modify with type safety&lt;/span&gt;
&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Build back to EDIFACT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildUseCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BuildEDIUseCase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ediString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buildUseCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapFromBusinessObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;standard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EDIFACT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is what "modern EDI" looks like.&lt;/strong&gt; ✨&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Project Stats 📊
&lt;/h2&gt;

&lt;p&gt;Let me give you a sneak peek at what we've built so far:&lt;/p&gt;

&lt;h3&gt;
  
  
  Packages (npm)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Current (EDIFACT):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;@ediflow/core&lt;/strong&gt; - Parser, Validator, Builder engine (MIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@ediflow/edifact-d96a&lt;/strong&gt; - D.96A (1996) - 89 messages (MIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@ediflow/edifact-d01b&lt;/strong&gt; - D.01B (2001) - 155 messages (MIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@ediflow/edifact-d20b&lt;/strong&gt; - D.20B (2020) - 178 messages (MIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@ediflow/edifact-d12a&lt;/strong&gt; - D.12A (2012) - 196 messages (MIT, Prep)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Coming Soon:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;@ediflow/x12&lt;/strong&gt; - ANSI ASC X12 support 🔜&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More formats&lt;/strong&gt; - We're agile, we follow where "Flow" leads us! 🌊&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Current Total:&lt;/strong&gt; 618 EDIFACT message types supported (and growing!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests &amp;amp; Quality
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;~600 tests&lt;/strong&gt; passing (unit + integration + e2e)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;~90% test coverage&lt;/strong&gt; (goal: 95%+)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Clean Architecture&lt;/strong&gt; - All layers tested independently&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;TypeScript strict mode&lt;/strong&gt; - No &lt;code&gt;any&lt;/code&gt; types!&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Zero dependencies&lt;/strong&gt; (core package)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────────┐
│     Presentation Layer                │  CLI, API, UI (Future)
│                                       │
│  ┌─────────────────────────────────┐ │
│  │   Infrastructure Layer          │ │  Parsers, Repositories
│  │                                 │ │
│  │  ┌───────────────────────────┐  │ │
│  │  │   Application Layer       │  │ │  Use Cases, Services
│  │  │                           │  │ │
│  │  │  ┌─────────────────────┐  │  │ │
│  │  │  │   Domain Layer      │  │  │ │  Entities, Value Objects
│  │  │  └─────────────────────┘  │  │ │
│  │  └───────────────────────────┘  │ │
│  └─────────────────────────────────┘ │
└───────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dependency direction:&lt;/strong&gt; Always &lt;strong&gt;inward&lt;/strong&gt; → → → →&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Presentation Layer (CLI, REST API, Web UI) is planned for future releases!&lt;/p&gt;




&lt;h2&gt;
  
  
  7. What's Coming in This Series? 🗓️
&lt;/h2&gt;

&lt;p&gt;This is just &lt;strong&gt;Part 1 of 6&lt;/strong&gt;! Here's what's next:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 1 (This Article): Why?&lt;/strong&gt; ← You are here! ✅
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Motivation &amp;amp; personal story&lt;/li&gt;
&lt;li&gt;Why Clean Architecture?&lt;/li&gt;
&lt;li&gt;Why TypeScript?&lt;/li&gt;
&lt;li&gt;Why Open Source?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 2 (Next Week): Domain Layer Deep-Dive&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Entities (EDIMessage, Segment)&lt;/li&gt;
&lt;li&gt;Value Objects (Delimiters, Standard, Version)&lt;/li&gt;
&lt;li&gt;Business Rules (ValidationRule, MandatoryRule)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real code examples&lt;/strong&gt; from EDIFlow&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 3: Application Layer - Use Cases &amp;amp; Services&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ParseEDIUseCase, ValidateMessageUseCase, BuildEDIUseCase&lt;/li&gt;
&lt;li&gt;ComposableValidationService&lt;/li&gt;
&lt;li&gt;DTOs &amp;amp; Dependency Injection&lt;/li&gt;
&lt;li&gt;How orchestration works&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 4: Infrastructure Layer &amp;amp; Monorepo Structure&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Parsers (EdifactMessageParser)&lt;/li&gt;
&lt;li&gt;Repositories (FileBasedMessageStructureRepository)&lt;/li&gt;
&lt;li&gt;Why 5 packages? (Monorepo strategy)&lt;/li&gt;
&lt;li&gt;TypeScript project references&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 5: Presentation Layer - The Interface&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CLI tools and commands&lt;/li&gt;
&lt;li&gt;REST API design (planned)&lt;/li&gt;
&lt;li&gt;Web UI vision (future)&lt;/li&gt;
&lt;li&gt;How Presentation Layer stays clean&lt;/li&gt;
&lt;li&gt;Dependency Injection from top to bottom&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 6 (Finale): Lessons Learned &amp;amp; Future&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Was Clean Architecture worth it? (honest answer!)&lt;/li&gt;
&lt;li&gt;Performance analysis (benchmarks!)&lt;/li&gt;
&lt;li&gt;Testing insights (600 tests!)&lt;/li&gt;
&lt;li&gt;What I'd do differently&lt;/li&gt;
&lt;li&gt;Roadmap &amp;amp; future plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Each article:&lt;/strong&gt; 1000-1800 words, deep technical dive with real code!&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Conclusion &amp;amp; Call to Action 🚀
&lt;/h2&gt;

&lt;h3&gt;
  
  
  My Journey
&lt;/h3&gt;

&lt;p&gt;EDIFlow is more than just a library. It's my journey of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📚 Learning Clean Architecture by &lt;strong&gt;doing&lt;/strong&gt;, not just reading&lt;/li&gt;
&lt;li&gt;💙 Proving TypeScript is perfect for enterprise-grade libraries&lt;/li&gt;
&lt;li&gt;🌍 Building in public and sharing the process&lt;/li&gt;
&lt;li&gt;🔧 Making "old school EDI" developer-friendly&lt;/li&gt;
&lt;li&gt;🌊 Following the &lt;strong&gt;Flow&lt;/strong&gt; - Starting with EDIFACT, next X12, then... who knows? We're agile!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  It's Not Perfect (Yet!)
&lt;/h3&gt;

&lt;p&gt;I'm not claiming EDIFlow is perfect. I've made mistakes. I've refactored. I've learned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And that's exactly what this series is about.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the next 4 articles, I'll show you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ What worked (spoiler: a lot!)&lt;/li&gt;
&lt;li&gt;❌ What didn't work (also a lot!)&lt;/li&gt;
&lt;li&gt;💡 How to apply Clean Architecture in TypeScript&lt;/li&gt;
&lt;li&gt;🚀 Real code from a production-ready library&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  This is Pair Programming... with You!
&lt;/h3&gt;

&lt;p&gt;I built EDIFlow with &lt;strong&gt;GitHub Copilot&lt;/strong&gt; as my pair programming partner. Now I'm sharing the journey with &lt;strong&gt;you&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow along, and let's learn together!&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🔗 Links &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Try EDIFlow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://www.npmjs.com/package/@ediflow/core" rel="noopener noreferrer"&gt;npm: @ediflow/core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;GitHub: ediflow-lib/core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;a href="https://github.com/ediflow-lib/core/blob/main/docs/QUICK_START.md" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Join the Community:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💬 &lt;a href="https://discord.gg/S5npqcv8Zu" rel="noopener noreferrer"&gt;Discord: EDIFlow Community&lt;/a&gt; - Real-time chat &amp;amp; support&lt;/li&gt;
&lt;li&gt;📣 &lt;a href="https://github.com/ediflow-lib/core/discussions" rel="noopener noreferrer"&gt;GitHub Discussions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📧 Email: &lt;a href="mailto:hello.ediflow@gmail.com"&gt;hello.ediflow@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read More:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📚 &lt;a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882" rel="noopener noreferrer"&gt;Clean Code by Robert C. Martin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164" rel="noopener noreferrer"&gt;Clean Architecture by Robert C. Martin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445" rel="noopener noreferrer"&gt;Agile Software Development by Robert C. Martin&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  💭 What Do YOU Want to Learn?
&lt;/h3&gt;

&lt;p&gt;This series is for &lt;strong&gt;you&lt;/strong&gt;. What topics should I cover?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain Layer patterns?&lt;/li&gt;
&lt;li&gt;TypeScript DI without frameworks?&lt;/li&gt;
&lt;li&gt;Testing strategies?&lt;/li&gt;
&lt;li&gt;Performance optimization?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Comment below!&lt;/strong&gt; 👇&lt;/p&gt;




&lt;h3&gt;
  
  
  📢 Next Up: Part 2 - Domain Layer
&lt;/h3&gt;

&lt;p&gt;Next week, we'll dive into the &lt;strong&gt;Domain Layer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do Entities differ from Value Objects?&lt;/li&gt;
&lt;li&gt;Why immutability matters&lt;/li&gt;
&lt;li&gt;Real code examples from EDIFlow&lt;/li&gt;
&lt;li&gt;TypeScript patterns that make Clean Architecture easier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;See you next week!&lt;/strong&gt; 🚀&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;If you found this helpful:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;a href="https://github.com/ediflow-lib/core" rel="noopener noreferrer"&gt;Star EDIFlow on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;a href="https://discord.gg/S5npqcv8Zu" rel="noopener noreferrer"&gt;Join our Discord&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 Follow this series (click "Follow" on my Dev.to profile!)&lt;/li&gt;
&lt;li&gt;🔄 Share with your network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Let's make EDI development great again!&lt;/strong&gt; 😄&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is part of the "Building EDIFlow: A Clean Architecture Journey in TypeScript" series. Follow along as we build a production-ready EDIFACT library from scratch using Clean Architecture principles.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Word Count:&lt;/strong&gt; ~1,650 words&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading Time:&lt;/strong&gt; ~7 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Series:&lt;/strong&gt; Part 1 of 6&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Coming Soon:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 2: Domain Layer Deep-Dive&lt;/li&gt;
&lt;li&gt;Part 3: Application Layer&lt;/li&gt;
&lt;li&gt;Part 4: Infrastructure &amp;amp; Packages&lt;/li&gt;
&lt;li&gt;Part 5: Presentation Layer&lt;/li&gt;
&lt;li&gt;Part 6: Lessons Learned&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>cleanarchitecture</category>
      <category>edifact</category>
      <category>x12</category>
    </item>
  </channel>
</rss>
