Skip to content

Commit a227ac3

Browse files
committed
docs(adr): add UMS schema-per-domain database boundary decision
1 parent 72d45af commit a227ac3

1 file changed

Lines changed: 190 additions & 0 deletions

File tree

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# ADR 0066: Database Schema per Domain for UMS Phase 1
2+
3+
## Status
4+
5+
Accepted
6+
7+
## Parent Standard
8+
9+
This ADR specializes the Evolith parent decision:
10+
11+
- [Evolith ADR 0067: Modular Monolith Database Boundary — Schema per Domain](https://github.com/beyondnetcode/evolith_arch32/blob/main/reference/architecture/adrs/core/0067-modular-monolith-schema-per-domain.md)
12+
13+
## Context and Problem Statement
14+
15+
UMS is implemented as both the official applied reference for Evolith and the Phase 1 product solution for enterprise user management.
16+
17+
The solution starts as a Modular Monolith, but it must avoid decisions that create unnecessary coupling or block a future migration toward distributed modules or microservices.
18+
19+
The key architectural question is whether UMS should use:
20+
21+
1. A single shared database without logical schema separation.
22+
2. A single physical database with schemas separated by module/domain.
23+
3. Multiple physical databases from Phase 1.
24+
25+
This decision is critical because it directly impacts:
26+
27+
- Domain responsibility separation.
28+
- Future migration to microservices.
29+
- Module independence.
30+
- Prevention of unnecessary coupling.
31+
- Formal Phase 1 ADR traceability.
32+
33+
## Decision
34+
35+
UMS Phase 1 will use:
36+
37+
```text
38+
Single physical SQL Server database + logical schemas per module/domain
39+
```
40+
41+
UMS will not start with multiple physical databases. However, it will also not use an unconstrained shared default schema for all modules.
42+
43+
Each bounded context or module must own its database schema.
44+
45+
Reference structure:
46+
47+
```text
48+
ums_database
49+
├── identity_schema
50+
├── access_schema
51+
├── tenant_schema
52+
├── audit_schema
53+
└── notification_schema
54+
```
55+
56+
The final schema names must align with the official UMS bounded context map and domain model.
57+
58+
## Architectural Rules for UMS
59+
60+
1. Each UMS module owns its schema and internal tables.
61+
2. A module must not directly mutate tables owned by another module.
62+
3. Cross-module interaction must happen through application services, explicit contracts, ports, domain services, or integration events.
63+
4. Cross-schema joins between modules are discouraged and require explicit architectural justification.
64+
5. EF Core mappings and migrations must preserve module ownership by schema.
65+
6. Any exception to schema ownership must be documented and reviewed architecturally.
66+
67+
## Rationale
68+
69+
This strategy preserves Phase 1 simplicity while enforcing architectural boundaries at the persistence layer.
70+
71+
A fully shared database schema would allow hidden coupling through direct table access, implicit joins, and unclear ownership. That would make the code look modular while the data model remains tightly coupled.
72+
73+
Using schemas per module/domain makes the Modular Monolith more honest and prepares UMS for future extraction when needed.
74+
75+
The intended evolution path is:
76+
77+
```text
78+
UMS Modular Monolith with schema-per-domain boundaries
79+
80+
Identify a bounded context that needs extraction
81+
82+
Move that schema to a dedicated database
83+
84+
Replace direct access with APIs, events, or integration contracts
85+
```
86+
87+
## Alternatives Considered
88+
89+
### Alternative 1: Single SQL Server database with one shared default schema
90+
91+
Example:
92+
93+
```text
94+
ums_database
95+
└── dbo
96+
```
97+
98+
**Advantages:**
99+
100+
- Simplest setup for the first implementation.
101+
- Fewer initial conventions and migration rules.
102+
103+
**Disadvantages:**
104+
105+
- High risk of hidden coupling.
106+
- Unclear table ownership.
107+
- Harder future service extraction.
108+
- Easier introduction of cross-domain joins and direct table dependencies.
109+
110+
**Result:** Rejected.
111+
112+
### Alternative 2: Physical database per module from Phase 1
113+
114+
Example:
115+
116+
```text
117+
ums_identity_db
118+
ums_access_db
119+
ums_audit_db
120+
ums_notification_db
121+
```
122+
123+
**Advantages:**
124+
125+
- Strongest physical isolation.
126+
- Clear deployment and ownership boundaries.
127+
128+
**Disadvantages:**
129+
130+
- Premature operational complexity for Phase 1.
131+
- More complex local development, backups, observability, transactions, migrations, and deployment.
132+
- Higher coordination cost before the product requires independent scaling or deployment.
133+
134+
**Result:** Rejected for Phase 1.
135+
136+
### Alternative 3: Single SQL Server database with schemas per module/domain
137+
138+
Example:
139+
140+
```text
141+
ums_database
142+
├── identity
143+
├── access
144+
├── tenant
145+
├── audit
146+
└── notification
147+
```
148+
149+
**Advantages:**
150+
151+
- Balances simplicity and architectural discipline.
152+
- Makes ownership explicit.
153+
- Reduces coupling risk.
154+
- Supports future microservice extraction.
155+
156+
**Disadvantages:**
157+
158+
- Requires schema naming and migration discipline.
159+
- Requires governance to avoid unauthorized cross-schema access.
160+
161+
**Result:** Accepted.
162+
163+
## Consequences
164+
165+
### Positive
166+
167+
- UMS has clear persistence ownership boundaries from Phase 1.
168+
- Module independence is reinforced beyond source-code structure.
169+
- Future migration to microservices becomes less disruptive.
170+
- Database coupling risks become visible and governable.
171+
- The decision remains aligned with Evolith parent architecture.
172+
173+
### Negative / Trade-offs
174+
175+
- Requires schema conventions from the beginning.
176+
- Requires careful EF Core configuration and migrations.
177+
- Teams must avoid using SQL Server as an informal integration mechanism.
178+
- Reporting or read-model scenarios may need explicit architectural exceptions.
179+
180+
## Implementation Guidance
181+
182+
- Define one EF Core schema mapping per bounded context/module.
183+
- Keep migrations traceable to the owning module/schema.
184+
- Avoid direct writes across schema boundaries.
185+
- Prefer application-layer contracts, integration events, or dedicated read models for cross-module scenarios.
186+
- Document any approved cross-schema dependency as an explicit exception.
187+
188+
## Final Rule
189+
190+
Each UMS module owns its schema. No module may directly access or modify another module's internal tables except through explicit, documented, and architecturally approved contracts.

0 commit comments

Comments
 (0)