Files
docs/hexa/wiki/05_philosophy/index.html

1136 lines
33 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="prev" href="../04_design/">
<link rel="next" href="../06_best_practices/">
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.23">
<title>Philosophy - Hexa Documentation</title>
<link rel="stylesheet" href="../assets/stylesheets/main.84d31ad4.min.css">
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="https://unpkg.com/dracula-prism/dist/css/dracula-prism.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="blue-grey" data-md-color-accent="teal">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#philosophy-why-type-declared-dependencies" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href=".." title="Hexa Documentation" class="md-header__button md-logo" aria-label="Hexa Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.25 2c.19 0 .36.11.44.26l2.22 3.96.09.28-.09.28-2.22 3.96c-.08.15-.25.26-.44.26h-4.5c-.19 0-.36-.11-.44-.26L3.09 6.78 3 6.5l.09-.28 2.22-3.96c.08-.15.25-.26.44-.26zm0 11c.19 0 .36.11.44.26l2.22 3.96.09.28-.09.28-2.22 3.96c-.08.15-.25.26-.44.26h-4.5c-.19 0-.36-.11-.44-.26l-2.22-3.96L3 17.5l.09-.28 2.22-3.96c.08-.15.25-.26.44-.26zm9.25-5.5c.19 0 .36.11.44.26l2.22 3.96.09.28-.09.28-2.22 3.96c-.08.15-.25.26-.44.26H15c-.19 0-.36-.11-.44-.26l-2.22-3.96-.09-.28.09-.28 2.22-3.96c.08-.15.25-.26.44-.26z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Hexa Documentation
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Philosophy
</span>
</div>
</div>
</div>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href=".." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../01_overview/" class="md-tabs__link">
Overview
</a>
</li>
<li class="md-tabs__item">
<a href="../02_components/" class="md-tabs__link">
Core Components
</a>
</li>
<li class="md-tabs__item">
<a href="../03_use_cases/01_yaml_first/" class="md-tabs__link">
Use Cases
</a>
</li>
<li class="md-tabs__item">
<a href="../04_design/" class="md-tabs__link">
Design
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="./" class="md-tabs__link">
Philosophy
</a>
</li>
<li class="md-tabs__item">
<a href="../06_best_practices/" class="md-tabs__link">
Best Practices
</a>
</li>
<li class="md-tabs__item">
<a href="../07_error_handling/" class="md-tabs__link">
Error Handling
</a>
</li>
<li class="md-tabs__item">
<a href="../08_testing_example/" class="md-tabs__link">
Testing
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href=".." title="Hexa Documentation" class="md-nav__button md-logo" aria-label="Hexa Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.25 2c.19 0 .36.11.44.26l2.22 3.96.09.28-.09.28-2.22 3.96c-.08.15-.25.26-.44.26h-4.5c-.19 0-.36-.11-.44-.26L3.09 6.78 3 6.5l.09-.28 2.22-3.96c.08-.15.25-.26.44-.26zm0 11c.19 0 .36.11.44.26l2.22 3.96.09.28-.09.28-2.22 3.96c-.08.15-.25.26-.44.26h-4.5c-.19 0-.36-.11-.44-.26l-2.22-3.96L3 17.5l.09-.28 2.22-3.96c.08-.15.25-.26.44-.26zm9.25-5.5c.19 0 .36.11.44.26l2.22 3.96.09.28-.09.28-2.22 3.96c-.08.15-.25.26-.44.26H15c-.19 0-.36-.11-.44-.26l-2.22-3.96-.09-.28.09-.28 2.22-3.96c.08-.15.25-.26.44-.26z"/></svg>
</a>
Hexa Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href=".." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../01_overview/" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../02_components/" class="md-nav__link">
<span class="md-ellipsis">
Core Components
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4" >
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Use Cases
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Use Cases
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_1" >
<label class="md-nav__link" for="__nav_4_1" id="__nav_4_1_label" tabindex="0">
<span class="md-ellipsis">
Contract Authoring
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_1">
<span class="md-nav__icon md-icon"></span>
Contract Authoring
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../03_use_cases/01_yaml_first/" class="md-nav__link">
<span class="md-ellipsis">
YAML First
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../03_use_cases/02_abc_first/" class="md-nav__link">
<span class="md-ellipsis">
ABC First
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_2" >
<label class="md-nav__link" for="__nav_4_2" id="__nav_4_2_label" tabindex="0">
<span class="md-ellipsis">
Wiring
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_2">
<span class="md-nav__icon md-icon"></span>
Wiring
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../03_use_cases/03_wiring_runtime/" class="md-nav__link">
<span class="md-ellipsis">
Wiring & Runtime
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../04_design/" class="md-nav__link">
<span class="md-ellipsis">
Design
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Philosophy
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Philosophy
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#one-idea" class="md-nav__link">
<span class="md-ellipsis">
One Idea
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#the-three-layers" class="md-nav__link">
<span class="md-ellipsis">
The Three Layers
</span>
</a>
<nav class="md-nav" aria-label="The Three Layers">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#layer-1-abc-contracts-samplesextraction_pipelineextraction_pipeline_abcpy" class="md-nav__link">
<span class="md-ellipsis">
Layer 1 — ABC contracts (samples/extraction_pipeline/extraction_pipeline_abc.py)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#layer-2-generic-concurrent-implementations-samplesextraction_pipelineextraction_pipeline_implpy" class="md-nav__link">
<span class="md-ellipsis">
Layer 2 — Generic concurrent implementations (samples/extraction_pipeline/extraction_pipeline_impl.py)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#layer-3-bank-specializations-samplesextraction_pipelinebanksaxisicicipdfpy-samplesextraction_pipelineextraction_pipeline_bankspy" class="md-nav__link">
<span class="md-ellipsis">
Layer 3 — Bank specializations (samples/extraction_pipeline/banks/{axis,icici}/pdf.py, samples/extraction_pipeline/extraction_pipeline_banks.py)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#the-re-pinning-cascade" class="md-nav__link">
<span class="md-ellipsis">
The Re-Pinning Cascade
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#the-two-fidelity-guarantees-this-pattern-preserves" class="md-nav__link">
<span class="md-ellipsis">
The Two Fidelity Guarantees This Pattern Preserves
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#what-this-pattern-is-not" class="md-nav__link">
<span class="md-ellipsis">
What This Pattern Is NOT
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#runtime-instantiation" class="md-nav__link">
<span class="md-ellipsis">
Runtime Instantiation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#related" class="md-nav__link">
<span class="md-ellipsis">
Related
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../06_best_practices/" class="md-nav__link">
<span class="md-ellipsis">
Best Practices
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../07_error_handling/" class="md-nav__link">
<span class="md-ellipsis">
Error Handling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../08_testing_example/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="philosophy-why-type-declared-dependencies">Philosophy — Why Type-Declared Dependencies<a class="headerlink" href="#philosophy-why-type-declared-dependencies" title="Permanent link">&para;</a></h1>
<p>This document explains the <em>why</em> behind the type-declared dependency pattern used for the extraction pipeline sample — specifically the Axis/Icici bank variation. It is, together with <a href="../04_design/">Design</a>, self-sufficient: a fresh agent should be able to pick up the codebase from here and reason about (and extend) the pattern without needing the original session context.</p>
<hr />
<h2 id="one-idea">One Idea<a class="headerlink" href="#one-idea" title="Permanent link">&para;</a></h2>
<blockquote>
<p><strong>A concrete class's annotated slot type <em>is</em> the dependency decision.</strong></p>
</blockquote>
<p>Bank variation is expressed entirely as <strong>class annotations</strong> — never as imperative wiring, never as <code>__init__</code> parameters, never as a composition-root that assembles objects by hand. The tree of ports a pipeline needs is declared once, and a bank differs from the generic pipeline <em>only</em> by which concrete types are pinned onto which slots.</p>
<hr />
<h2 id="the-three-layers">The Three Layers<a class="headerlink" href="#the-three-layers" title="Permanent link">&para;</a></h2>
<p>The pattern is built from three fixed layers. Understanding which layer something belongs to is the whole mental model.</p>
<h3 id="layer-1-abc-contracts-samplesextraction_pipelineextraction_pipeline_abcpy">Layer 1 — ABC contracts (<code>samples/extraction_pipeline/extraction_pipeline_abc.py</code>)<a class="headerlink" href="#layer-1-abc-contracts-samplesextraction_pipelineextraction_pipeline_abcpy" title="Permanent link">&para;</a></h3>
<p>Authoritative <em>declaration of need</em> ("WHAT a stage requires").</p>
<ul>
<li>Each port is an <code>ABC</code>.</li>
<li>It declares <strong>attributes</strong> as class-level annotated defaults (e.g. <code>TransactionParserPort.min_numbers: int = 3</code>), optionally grouped as a nested <strong>struct</strong> via a <code>@dataclass</code> (e.g. <code>source: ExtractionSource</code>).</li>
<li>It declares <strong>port slots</strong> as annotated attributes whose type is another ABC (e.g. <code>TransactionParserPort.number: NumberPort</code>, <code>amount_balance: AmountBalancePort</code>).</li>
<li>It declares <strong><code>@abstractmethod</code> bodies</strong> — the method signatures each concrete implementation must provide.</li>
</ul>
<p>The ABC never runs, never holds an instance, and never says <em>which</em> implementation to use. It only says <em>what the shape is</em>.</p>
<h3 id="layer-2-generic-concurrent-implementations-samplesextraction_pipelineextraction_pipeline_implpy">Layer 2 — Generic concurrent implementations (<code>samples/extraction_pipeline/extraction_pipeline_impl.py</code>)<a class="headerlink" href="#layer-2-generic-concurrent-implementations-samplesextraction_pipelineextraction_pipeline_implpy" title="Permanent link">&para;</a></h3>
<p>The <strong>common default behavior</strong> ("Impl* — the shared adaptor").</p>
<ul>
<li><code>class ImplTransactionParserPort(TransactionParserPort)</code> subclasses the ABC, implements every <code>@abstractmethod</code>, and <strong>re-pins its port slots to concrete <code>Impl*</code> types</strong>: <code>number: ImplNumberPort</code>, <code>desc: ImplDescPort</code>, <code>amount_balance: ImplAmountBalancePort</code>.</li>
<li><code>ImplAmountBalancePort</code> in turn pins <code>number: ImplNumberPort</code> and <code>ambiguity: ImplAmbiguityPort</code>.</li>
<li>The root, <code>ImplExtractionPipeline</code>, pins its five stage slots to <code>Impl*</code> ports: <code>ingest</code>, <code>raw_lines</code>, <code>txn_blocks</code>, <code>txn_dicts</code>, <code>raw_expense</code>.</li>
</ul>
<p><code>Impl*</code> is the <strong>default that shared, bank-agnostic behavior lives in</strong>. Any slot a bank does <em>not</em> override falls back to <code>Impl*</code> at runtime.</p>
<h3 id="layer-3-bank-specializations-samplesextraction_pipelinebanksaxisicicipdfpy-samplesextraction_pipelineextraction_pipeline_bankspy">Layer 3 — Bank specializations (<code>samples/extraction_pipeline/banks/{axis,icici}/pdf.py</code>, <code>samples/extraction_pipeline/extraction_pipeline_banks.py</code>)<a class="headerlink" href="#layer-3-bank-specializations-samplesextraction_pipelinebanksaxisicicipdfpy-samplesextraction_pipelineextraction_pipeline_bankspy" title="Permanent link">&para;</a></h3>
<p>The <strong>variation</strong> ("what differs per bank").</p>
<ul>
<li>Bank classes subclass the <strong>concrete <code>Impl*</code> classes, never the ABC</strong> — they <em>extend</em>, they never <em>reinvent</em> the contract.</li>
<li>Each <code>Axis*</code>/<code>Icici*</code> class overrides <strong>only the members that differ</strong> from the generic <code>Impl*</code>; everything shared is inherited.</li>
<li>A bank root is a <strong>thin annotated subclass of <code>ImplExtractionPipeline</code></strong> that re-pins only the slots that differ:</li>
</ul>
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="k">class</span><span class="w"> </span><span class="nc">AxisExtractionPipeline</span><span class="p">(</span><span class="n">ImplExtractionPipeline</span><span class="p">):</span>
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> <span class="n">txn_dicts</span><span class="p">:</span> <span class="n">AxisTxnDictsPort</span>
</code></pre></div>
<hr />
<h2 id="the-re-pinning-cascade">The Re-Pinning Cascade<a class="headerlink" href="#the-re-pinning-cascade" title="Permanent link">&para;</a></h2>
<p>Bank-specific wiring is <strong>hierarchical and cascading</strong>. Each level narrows exactly one slot, and its narrowed type drags the next level's narrowing along with it:</p>
<div class="language-text highlight"><span class="filename">Text Only</span><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>AxisExtractionPipeline
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a> └─ txn_dicts: AxisTxnDictsPort (only override on the root)
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a> └─ parser: AxisTransactionParserPort
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a> ├─ number: AxisNumberPort (get_numbers → all clean nums)
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> ├─ desc: AxisDescPort (STARTTERS)
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a> └─ amount_balance: AxisAmountBalancePort
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> └─ number: AxisNumberPort (re-pin: same bank's number port)
</code></pre></div>
<p>Siblings that are already correct in <code>Impl*</code> are <strong>inherited, not redeclared</strong>. E.g. the parser's <code>date</code> slot comes from <code>ImplTransactionParserPort</code>; the amount-balance port's <code>ambiguity</code> comes from <code>ImplAmountBalancePort</code>. Re-declaring them adds mental load on the extender with zero behavior change.</p>
<blockquote>
<p><strong>Rule — only redeclare the slot you are actually changing.</strong> If a slot's behavior is already correct from <code>Impl*</code>, leave it out.</p>
</blockquote>
<hr />
<h2 id="the-two-fidelity-guarantees-this-pattern-preserves">The Two Fidelity Guarantees This Pattern Preserves<a class="headerlink" href="#the-two-fidelity-guarantees-this-pattern-preserves" title="Permanent link">&para;</a></h2>
<ol>
<li><strong>Shared behavior stays shared.</strong> Slots like <code>ingest</code>, <code>raw_lines</code>, <code>txn_blocks</code>, <code>raw_expense</code>, parser <code>date</code>, and <code>ambiguity</code> are bank-agnostic. Leaving them at the <code>Impl*</code> default is deliberate, not an omission.</li>
<li><strong>Banks never reinvent.</strong> <code>Axis*</code>/<code>Icici*</code> only <em>extend</em> <code>Impl*</code>. The ABC contract in <code>samples/extraction_pipeline/extraction_pipeline_abc.py</code> is defined once and shared by every bank.</li>
</ol>
<hr />
<h2 id="what-this-pattern-is-not">What This Pattern Is NOT<a class="headerlink" href="#what-this-pattern-is-not" title="Permanent link">&para;</a></h2>
<ul>
<li><strong>No imperative assembly.</strong> No <code>build_axis_pipeline()</code> wiring objects by hand.</li>
<li><strong>No constructor injection.</strong> <code>Impl*</code> classes have no <code>__init__</code>; wiring is done by assigning concrete port instances onto annotated slots.</li>
<li><strong>YAML and ABCs are interchangeable, and both optional.</strong> The <code>.yaml</code> spec and the <code>_abc.py</code> module are two representations of the <em>same contract</em>. hexa is agnostic of the extraction pipeline sample and never imposes a workflow: a user can hand-write the ABC file, start from YAML (ABC generated), start from an ABC file (YAML emitted), or use both as a spec. The optional utilities in <a href="../04_design/">Design</a> § <em>Utilities</em> translate between them and can verify they agree — but the bank <code>Impl*</code>/specialization files are always hand-written (their slots are a manual choice) and never generated.</li>
</ul>
<hr />
<h2 id="runtime-instantiation">Runtime Instantiation<a class="headerlink" href="#runtime-instantiation" title="Permanent link">&para;</a></h2>
<p>The annotations are the decision; <code>hexa.build(cls)</code> (see <a href="../04_design/">Design</a> § <em>Container</em>) turns a root class into a wired instance by reading <code>__annotations__</code> + MRO, recursively instantiating each pinned concrete type, and <code>setattr</code>ing the child onto the parent slot. This document is scoped to the <em>notation</em> — the rest of the tree can be expressed purely as class annotations and filled at runtime by the container.</p>
<hr />
<h2 id="related">Related<a class="headerlink" href="#related" title="Permanent link">&para;</a></h2>
<ul>
<li><a href="../04_design/">04 Design</a> · <a href="../02_components/">02 Core Components</a> · <a href="../01_overview/">Overview</a></li>
</ul>
</article>
</div>
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../04_design/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Design">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Design
</div>
</div>
</a>
<a href="../06_best_practices/" class="md-footer__link md-footer__link--next" aria-label="Next: Best Practices">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Best Practices
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "..", "features": ["navigation.tabs", "navigation.expand", "navigation.sections", "navigation.footer", "navigation.top", "navigation.instant", "toc.integrate", "content.code.annotate", "content.code.copy", "content.code.select", "content.code.line_numbers", "content.tabs.link", "content.tooltips", "content.action.edit", "search.suggest", "search.highlight", "search.share", "header.autohide", "announce.dismiss", "footer.social"], "search": "../assets/javascripts/workers/search.973d3a69.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../assets/javascripts/bundle.f55a23d4.min.js"></script>
</body>
</html>