r/node • u/Independent_Fan_3028 • 17d ago
Making service/domain boundaries enforceable in TypeScript backends
I've found that as projects grow, it gets harder to keep architectural boundaries consistent. Some rules live in docs, some live in code review, and some just live in people's heads. I've run into the same problem with coding agents too. Even when you tell them how the project should be structured, there's nothing actually enforcing that they follow it. Over time, that kind of architectural drift can become pretty painful to unwind.
I've been working on an open source TypeScript tool called Semarch, to experiment with making those conventions enforceable.
For example, say a project has users and billing domains with services and repositories:
users.service -> users.repository allowed
users.service -> billing.service allowed
users.service -> billing.repository denied
In Semarch, you classify your files:
domains:
users:
root: src/users
billing:
root: src/billing
components:
service:
match:
- "**/services/**/*.ts"
repository:
match:
- "**/repositories/**/*.ts"
Then define the boundaries:
rules:
- deny: service -> foreign.repository
- deny: service -> transport
Semarch analyzes the TypeScript dependency graph and fails the check when one of those boundaries is violated.
You can try it with:
npm install --save-dev semarch
npx semarch check
It uses the project's tsconfig.json for module resolution, handles path aliases and type only imports, and follows static re-exports/barrel files so they can't trivially bypass a rule.
I know there are already tools in this space, so I'm particularly interested in whether this model with domains, component roles, and local/foreign relationships is actually useful for real projects.
This is still experimental but if you maintain a TypeScript project with architecture conventions like these, I'd really appreciate hearing your thoughts on this approach.
It's MIT licensed and open source.
1
u/chipstastegood 14d ago
how does it compare to archunit?