General Direction¶
Date¶
2026-04-05
Context¶
The project is growing, and current architecture has friction in:
- performance overhead from heavy internal model validation/conversion
- persistence coupling inside services
- lack of migration-friendly persistence boundaries
- upcoming long-term async goal
Decisions¶
-
Architecture pattern
- Use Enhanced Service Layer with selective DDD concepts.
- Do not adopt MVC.
- Do not adopt full DDD.
-
Persistence architecture
- Introduce Repository pattern as the main decoupling layer.
- Services should call repositories, not DB session/query primitives directly.
-
SQL stack direction
- Move from SQLModel-centered persistence toward SQLAlchemy-first persistence.
- Use SQLAlchemy ORM for domain entities.
- Use SQLAlchemy Core selectively for heavy reporting/aggregation queries.
- Add Alembic after SQLAlchemy ORM baseline is in place.
-
Model/validation direction
- Reduce internal Pydantic usage gradually for performance.
- Keep strict validation at external boundaries where it still adds value.
- Prefer lightweight internal structures (dataclasses/value objects, then attrs).
-
attrs/cattrs decision
- attrs + cattrs are a good fit for this project.
- Use cattrs hooks for Decimal/date/enum/nested conversions.
- Replace manual display mapping and RootModel batch wrappers with cattrs-based conversion.
-
Async decision
- Async is a future goal, not immediate.
- Prepare async-ready repository contracts later, after persistence boundaries stabilize.
Alternatives Considered¶
- MVC: rejected (UI-centric, weak fit for CLI/service layering)
- Full DDD: rejected (too heavy for current scale)
- SQLAlchemy Core-only: rejected as default (less ergonomic for domain CRUD)
Consequences¶
Positive¶
- safer long-running migration
- clearer domain/persistence boundaries
- better performance trajectory
- easier async adoption later
Negative¶
- temporary dual-path complexity during migration
- additional contract/integration testing required during transitions