Skip to main content
Matcher is a modular monolith with Domain-Driven Design (DDD) and hexagonal architecture. CQRS separates commands (writes) from queries (reads). This keeps operations simple while maintaining clear boundaries. Each module can evolve independently without the complexity of microservices.

Architecture overview


Matcher Architecture

Matcher architecture overview

Bounded contexts


Matcher is split into modules. Each owns its data and exposes clean interfaces to the others.
  • Configuration: What you’re reconciling (contexts, sources, field maps, rules)
  • Discovery: External data source connections, schema detection, and extraction orchestration with Matcher’s embedded extraction engine
  • Ingestion: Getting data in (parsing, validation, normalization)
  • Matching: The engine (rule execution, confidence scoring)
  • Exception: Handling unmatched items (workflow, assignment, resolution)
  • Governance: Audit trails (append-only logs for compliance)
  • Reporting: Visibility (reports, exports, dashboards)

Configuration

Defines what you’re reconciling and how. Handles:
  • Contexts (what you reconcile)
  • Sources (where data comes from)
  • Field maps (translating external fields)
  • Rules (how to match)
Key models:
  • ReconciliationContext: The reconciliation scope
  • ReconciliationSource: Source configuration
  • FieldMap: Field translation rules
  • MatchRule: Matching logic

Discovery

The Discovery bounded context manages external data source connectivity, schema detection, and extraction orchestration with the extraction engine. Discovery runs inside Matcher. Responsibilities:
  • Manage external data source connections
  • Detect and cache source schemas
  • Run in-process extractions and hand results directly to Ingestion
  • Track connection and extraction lifecycles
  • Manage REST API connections and keep each connection’s schema history
Key entities:
  • ExtractionRequest: Tracks an extraction lifecycle run by the embedded engine
See Discovery for how Discovery connects to external databases with the extraction engine.

Ingestion

The Ingestion bounded context handles data intake and normalization. Responsibilities:
  • Parse uploaded files
  • Validate incoming data against configured schemas
  • Normalize external data into a canonical representation
  • Detect and handle duplicate records
  • Emit domain events when ingestion completes
Key entities:
  • IngestionJob: Tracks ingestion lifecycle and status
  • Transaction: Normalized canonical transaction record
Events published:
  • ingestion.completed: Indicates data readiness for matching

Matching

The Matching bounded context contains the reconciliation engine. Responsibilities:
  • Load applicable rules for a reconciliation context
  • Execute matching strategies (exact, tolerance, fuzzy, date-based)
  • Calculate confidence scores
  • Create match groups and allocate transactions
  • Identify unmatched transactions
Key entities:
  • MatchRun: Execution of a matching job
  • MatchGroup: Group of reconciled transactions
  • MatchItem: Individual transaction allocation
Events published:
  • match_group.confirmed: A match group has been finalized
  • match_group.unmatched: A previously confirmed match was reverted
  • transaction.pending_review: A non-automatic candidate needs review

Exception management

The Exception bounded context manages unresolved transactions. Responsibilities:
  • Classify exceptions by severity
  • Assign exceptions to your team
  • Support manual overrides, adjustments, and disputes
  • Track resolution status
  • Receive resolution callbacks from external systems
Key entities:
  • Exception: An unresolved transaction

Governance

The Governance bounded context preserves reconciliation traceability. Responsibilities:
  • Record configuration changes and exception resolutions in append-only audit logs
  • Provide queryable audit history
  • Support regulatory and compliance reporting
Key entities:
  • AuditLog: Append-only record of instrumented auditable mutation workflows
Audit logs are append-only. Archival exports old monthly partitions to object storage and then drops them from the database.

Reporting

The Reporting bounded context provides operational visibility. Responsibilities:
  • Generate reconciliation reports
  • Expose dashboard metrics
  • Export reconciliation data in multiple formats
Key entities:
  • Report: Reconciliation summary
  • Dashboard: Aggregated operational metrics
  • ExportJob: Asynchronous export execution

Data flow


Reconciliation follows a deterministic pipeline across bounded contexts:
1

Configuration

You define reconciliation contexts, sources, field mappings, and rules through the API.
2

Discovery

Discovery connects to external sources, detects their schemas, and runs extractions in-process with the extraction engine. Discovery hands extracted results directly to Ingestion.
3

Ingestion

Ingestion parses, validates, normalizes, and deduplicates uploaded files and the data that Discovery extracts. Ingestion emits an ingestion.completed event.
4

Matching

Matching applies rules to eligible transactions and produces match groups with confidence scores on an integer scale of 0 to 100. Groups from EXACT and TOLERANCE rules with a confidence of at least 90 out of 100 auto-confirm. Groups from FUZZY and DATE_LAG rules always require manual review. Unmatched items become exceptions.
5

Exception handling

The Exception context classifies exceptions, and your team assigns and resolves them. External systems can report resolutions through callbacks. Resolution updates return to Matcher.
6

Governance

Governance records configuration changes and exception resolutions in append-only audit logs.
7

Reporting

Users access reports and dashboards showing reconciliation status, match rates, and exception aging.

Infrastructure components


Matcher relies on the following infrastructure services:

Database architecture

  • Eventual consistency for reporting views

Tenant resolution

Matcher resolves the tenant as follows:
  • Matcher takes tenant identity from the tenant_id or tenantId claim of the bearer token
  • Matcher never accepts tenant identifiers from request parameters
For details, see Security.

Design patterns


Outbox pattern

Matcher uses per-event delivery policies. Matcher persists an outbox record for outbox-backed events and dispatches them asynchronously. Other events can use direct delivery with an outbox fallback when the circuit is open.

Next steps


Quick start

Explore the architecture through a guided example.

Security

Review authentication, authorization, and tenant isolation mechanisms.