Skip to main content
Version: 0.2.3

Architectural Constraints

Hard Rules

These constraints are hard rules. Breaking them is considered a design error. They protect the core from coupling and ensure Sercha remains extensible, testable, and maintainable for years to come.

Constraint A: Connectors Are Plugin-Like

RuleEnforcement
Adding a connector requires zero changes to coreImplements interface only
Connectors live in isolated packagesinternal/connectors/<name>/
Single registration pointOne line in main.go
Services never check connector typeUse factory abstraction
Multiple instances per typeFactory creates from config

Constraint B: Normalisers Are Plugin-Like

RuleEnforcement
New MIME types require zero core changesImplements interface only
Generic normalisers in normalisers/PDF, HTML, Markdown
Connector-specific in connector packageGmail emails, Slack messages
Selection via registryMIME type + priority
Services never check MIME typeRegistry handles dispatch

Constraint C: Services Are 100% Generic

Services (including SyncOrchestrator and Scheduler) must be completely agnostic to what they process.

AllowedForbidden
factory.Create(source)if source.Type == "gmail"
connector.Fetch()switch connector.(type)
registry.Normalise(raw)if raw.MIME == "pdf"
store.Save(doc)Type assertions

Constraint D: Core Remains Pure

The core/ package has no knowledge of implementations.

PackageMay ImportMust NOT Import
core/domainStandard libraryAny internal package
core/ports/drivencore/domainAny adapter
core/ports/drivingcore/domainAny adapter
core/servicescore/domain, core/portsAny adapter

Dependencies point inward. Adapters depend on ports. Core depends on nothing external.

Audit Checklist

RequirementVerification
Connectors are plugin-likeZero core changes for new connector
Multiple instances per typeFactory creates from config
Normalisers are plugin-likeZero core changes for new format
Normaliser priority worksConnector-specific > MIME > fallback
Services are genericNo type checks, no adapter imports
Core is pureNo adapter imports in core/
Scheduler uses driven portSQLite adapter for task persistence
Single wiring pointAll registration in main.go
Boundaries enforcedDirectory structure matches
Driving adapters don't import core/servicesUse driving ports only

Enforcement

MethodPurpose
Code reviewCheck against audit list
Import lintingCI rejects forbidden imports
Architecture testsVerify dependency direction
This documentSource of truth

Next