From 170308ba051bda4ac324ad60b688660bc9d4e0ab Mon Sep 17 00:00:00 2001 From: AzuTear Date: Sun, 28 Jun 2026 09:48:34 +0200 Subject: [PATCH] docs: define repository pattern boundary --- docs/ARCHITECTURE.md | 22 +++++++++++ docs/CONVENTIONS.md | 10 ++++- docs/DECISIONS.md | 60 ++++++++++++++++++++++++++++++ examples/ARCHITECTURE.example.md | 13 +++++++ examples/CONVENTIONS.example.md | 4 ++ examples/DECISIONS.example.md | 52 ++++++++++++++++++++++++++ templates/ARCHITECTURE.template.md | 8 ++++ templates/CONVENTIONS.template.md | 4 ++ 8 files changed, 171 insertions(+), 2 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index d8f385c..228f4ff 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -97,6 +97,28 @@ Domain code should not depend on delivery mechanisms, databases, network clients, or framework-specific runtime concerns unless the project intentionally uses an architecture where that trade-off is accepted and recorded. +## Persistence Boundary + +The default persistence boundary is the repository pattern. + +Use repositories to express domain-specific persistence operations without +leaking database, ORM, file-system, or external storage details into application +or domain logic. + +Default rules: + +- Application or domain-facing code depends on repository interfaces. +- Infrastructure implements repository interfaces. +- Repositories are named around domain concepts, not database tables. +- Repositories expose intention-revealing operations, not generic CRUD by + default. +- Transaction boundaries are explicit at the application service, unit of work, + or framework boundary selected by the project. + +If a project intentionally avoids repositories because the framework or +architecture provides a better persistence boundary, record that decision in +[DECISIONS.md](DECISIONS.md). + ## Request Flow ```mermaid diff --git a/docs/CONVENTIONS.md b/docs/CONVENTIONS.md index 2d605eb..8185ec8 100644 --- a/docs/CONVENTIONS.md +++ b/docs/CONVENTIONS.md @@ -125,9 +125,15 @@ Project-specific DTO rules: ## Repository Rules -Repositories, gateways, or data access abstractions should express persistence -intent without hiding important consistency behavior. +Use the repository pattern as the default persistence boundary. +Repositories, gateways, or data access abstractions should express domain +persistence intent without hiding important consistency behavior. + +- Define repository interfaces at the application or domain boundary. +- Implement repositories in infrastructure or persistence-specific modules. +- Prefer specific repositories such as `OrderRepository` or + `SupportRequestRepository`. - Keep query methods specific enough to reveal purpose. - Avoid generic repositories when they obscure domain behavior. - Document transaction boundaries. diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 25e6d64..853955f 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -25,6 +25,7 @@ For project constraints, see [PROJECT.md](PROJECT.md). | ADR-001 | Example: Start with a modular monolith | Example | 2026-06-28 | | ADR-002 | Example: Keep project documentation in the repository | Example | 2026-06-28 | | ADR-003 | Example: Require explicit validation evidence before merge | Example | 2026-06-28 | +| ADR-004 | Example: Use repository pattern for persistence boundaries | Example | 2026-06-28 | ## ADR-000: ADR Template @@ -245,3 +246,62 @@ manual checks, or reasons validation could not be run. - PR templates should include validation. - Reviewers can block merges when risk is high and evidence is missing. - Failed or skipped validation must be explained. + +## ADR-004: Example: Use Repository Pattern For Persistence Boundaries + +### Title + +Use repository pattern for persistence boundaries. + +### Status + +Example + +### Date + +2026-06-28 + +### Context + +The project needs domain and application logic to remain independent from the +database engine, ORM, and storage implementation details. + +### Problem + +Without an explicit persistence boundary, database concerns can leak into +business logic and make testing, refactoring, and future storage changes harder. + +### Alternatives + +| Alternative | Summary | +| --- | --- | +| Direct ORM access everywhere | Fast initially, but couples business logic to persistence details. | +| Generic repository | Reduces ORM exposure, but often becomes a thin CRUD wrapper. | +| Domain-specific repository pattern | Keeps persistence behind intention-revealing interfaces. | + +### Pros + +- Keeps application and domain logic focused on use cases and rules. +- Makes persistence behavior easier to test and replace. +- Creates a clear place for query intent and transaction expectations. +- Reduces accidental coupling to ORM-specific APIs. + +### Cons + +- Adds interfaces and implementation classes. +- Poorly designed repositories can hide important query or transaction costs. +- Generic repositories can obscure domain behavior if used mechanically. + +### Decision + +Use domain-specific repositories as the default persistence boundary. + +### Consequences + +- Repository interfaces should live at the application or domain boundary. +- Repository implementations should live in infrastructure or persistence + modules. +- Generic CRUD repositories should be avoided unless the project records a clear + reason. +- Transaction boundaries must be documented where writes span multiple + repositories. diff --git a/examples/ARCHITECTURE.example.md b/examples/ARCHITECTURE.example.md index 712ac9a..62050ec 100644 --- a/examples/ARCHITECTURE.example.md +++ b/examples/ARCHITECTURE.example.md @@ -78,6 +78,19 @@ atlas-desk/ - Domain rules do not depend on HTTP, Vue, or database APIs. - Infrastructure implementations depend inward on application contracts. +## Persistence Boundary + +Atlas Desk uses the repository pattern for persistence boundaries. + +- Repository interfaces live in the owning backend module. +- Repository implementations live in the module's infrastructure or persistence + folder. +- Repositories are domain-specific, such as `RequestRepository` and + `UserRepository`. +- Application services coordinate transactions for workflows that write multiple + records, such as assignment plus audit event creation. +- Generic CRUD repositories are not used because they hide workflow intent. + ## Request Flow ```mermaid diff --git a/examples/CONVENTIONS.example.md b/examples/CONVENTIONS.example.md index 52114da..b44324a 100644 --- a/examples/CONVENTIONS.example.md +++ b/examples/CONVENTIONS.example.md @@ -57,6 +57,10 @@ ## Repository Rules +- Use the repository pattern for persistence boundaries. +- Keep repository interfaces in the owning backend module. +- Keep repository implementations near persistence infrastructure. +- Use specific repositories such as `RequestRepository` and `UserRepository`. - Keep queries explicit, such as `FindOpenRequestsForQueue`. - Do not add generic repository abstractions over the database context. - Write operations that can be retried must be idempotent or transactionally protected. diff --git a/examples/DECISIONS.example.md b/examples/DECISIONS.example.md index f82ab97..359a1a7 100644 --- a/examples/DECISIONS.example.md +++ b/examples/DECISIONS.example.md @@ -148,3 +148,55 @@ Enforce authorization in backend policies for all sensitive actions. - Frontend checks remain usability hints only. - Authorization tests are required for each protected workflow. - Policy changes must be reviewed as security-sensitive changes. + +## ADR-004: Use Repository Pattern For Persistence Boundaries + +### Status + +Accepted + +### Date + +2026-06-28 + +### Context + +Atlas Desk uses PostgreSQL and Entity Framework Core, but request workflow rules +should not depend directly on ORM APIs or database-specific query details. + +### Problem + +Direct persistence access from application services would make workflow logic +harder to test and increase coupling to storage implementation details. + +### Alternatives + +| Alternative | Summary | +| --- | --- | +| Direct database context access | Simple, but spreads persistence concerns through application logic. | +| Generic repository | Hides the ORM, but exposes low-value CRUD abstractions. | +| Domain-specific repositories | Keeps persistence behind workflow-focused interfaces. | + +### Pros + +- Keeps workflow services focused on use cases. +- Makes request assignment and audit behavior easier to test. +- Provides intention-revealing query methods. +- Allows persistence implementation changes without rewriting domain logic. + +### Cons + +- Adds interface and implementation classes. +- Requires discipline to avoid generic CRUD repositories. +- Complex queries still need explicit performance review. + +### Decision + +Use domain-specific repositories for persistence boundaries. + +### Consequences + +- `RequestRepository` owns request persistence operations. +- `UserRepository` owns user lookup persistence operations. +- Reporting uses explicit read queries instead of mutating request data. +- Transaction boundaries remain visible in application services. diff --git a/templates/ARCHITECTURE.template.md b/templates/ARCHITECTURE.template.md index f450de4..822df0f 100644 --- a/templates/ARCHITECTURE.template.md +++ b/templates/ARCHITECTURE.template.md @@ -45,6 +45,14 @@ flowchart LR - {{DEPENDENCY_RULE_2}} - {{DEPENDENCY_RULE_3}} +## Persistence Boundary + +- Pattern: Repository pattern +- Repository interfaces: {{REPOSITORY_INTERFACE_LOCATION}} +- Repository implementations: {{REPOSITORY_IMPLEMENTATION_LOCATION}} +- Transaction boundary: {{TRANSACTION_BOUNDARY}} +- Repository rules: {{REPOSITORY_RULES}} + ## Request Flow {{REQUEST_FLOW}} diff --git a/templates/CONVENTIONS.template.md b/templates/CONVENTIONS.template.md index 85b6951..9bdce1c 100644 --- a/templates/CONVENTIONS.template.md +++ b/templates/CONVENTIONS.template.md @@ -34,6 +34,10 @@ ## Repository Rules +- Use the repository pattern for persistence boundaries. +- Repository interfaces live in {{REPOSITORY_INTERFACE_LOCATION}}. +- Repository implementations live in {{REPOSITORY_IMPLEMENTATION_LOCATION}}. +- Repositories should be domain-specific, not generic CRUD wrappers by default. - {{REPOSITORY_RULE}} ## Service Rules