Skip to main content
Matcher is built as a modular monolith using 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 has seven 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 the embedded Fetcher engine
  • Ingestion: Getting data in (parsing, validation, normalization)
  • Matching: The engine (rule execution, confidence scoring)
  • Exception: Handling unmatched items (workflow, routing, resolution)
  • Governance: Audit trails (immutable logs for compliance)
  • Reporting: Visibility (reports, exports, dashboards)

Configuration

Defines what you’re reconciling and how. Handles:
  • Contexts (what’s being reconciled)
  • 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 Fetcher’s embedded engine. Matcher hosts that engine in-process; Fetcher is not a remote service. 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
Key entities:
  • FetcherConnection: External source connection managed locally by the embedded engine
  • ExtractionRequest: Tracks an extraction lifecycle run by the embedded engine
See Discovery for how Discovery connects to external databases with the embedded Fetcher engine.

Ingestion

The Ingestion bounded context handles data intake and normalization. Responsibilities:
  • Parse uploaded files (CSV, JSON, XML)
  • 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, 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
  • Route exceptions to internal teams or external systems
  • Support manual overrides and adjustments
  • Track resolution status and SLAs
  • Integrate with external workflow tools
Key entities:
  • Exception: An unresolved transaction
  • Resolution: Outcome of exception handling
  • RoutingRule: Routing and escalation logic
Integrations:
  • JIRA for issue tracking
  • Webhooks for custom workflows
ServiceNow is an accepted routing target, but its HTTP connector is not implemented.

Governance

The Governance bounded context preserves reconciliation traceability. Responsibilities:
  • Record instrumented auditable mutation workflows in immutable 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 by design. Entries cannot be modified or removed to preserve compliance integrity.

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

Reconciliation contexts, sources, field mappings, and rules are defined through the API.
2

Discovery

Discovery connects to external sources, detects their schemas, and runs extractions in-process with the embedded Fetcher engine. Extracted results are handed directly to Ingestion.
3

Ingestion

Uploaded files and data extracted by Discovery are parsed, validated, normalized, and deduplicated. An ingestion.completed event is emitted.
4

Matching

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

Exception handling

Exceptions are classified, routed, and resolved either manually or via external systems. Resolution updates are propagated back to Matcher.
6

Governance

Instrumented auditable mutation workflows across the pipeline are recorded in immutable 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

  • Tenant-specific pool resolution in configured multi-tenant deployments for data separation
  • Strong consistency for matching and exception state
  • Eventual consistency for reporting views

Multi-tenancy

Matcher enforces strict tenant isolation:
  • With AUTH_PROVIDER=plugin-auth, tenant identity is extracted from tenant_id or tenantId JWT claims
  • workos, single-tenant, and authentication-disabled deployments use the configured default tenant
  • Tenant identifiers are never accepted via request parameters
  • Database access is scoped through the active tenant’s connection pool
  • All queries are automatically constrained to the active tenant
This model prevents cross-tenant data access and supports regulatory and audit requirements.

Design patterns


Hexagonal architecture

Each bounded context follows the ports-and-adapters pattern:

Cqrs-light

Write and read paths are separated at the service level:
  • *_commands.go for state mutations
  • *_queries.go for read operations
This improves code organization and allows independent optimization of query paths.

Outbox pattern

Matcher uses per-event delivery policies. Outbox-backed events persist an outbox record and are dispatched 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.