Skip to content
This repository was archived by the owner on Jul 26, 2026. It is now read-only.
This repository was archived by the owner on Jul 26, 2026. It is now read-only.

[Architecture] Replace the global IUnitOfWork service bag with feature-scoped transactional ports #38

Description

@mhbdev

Summary

IUnitOfWork currently exposes every repository in the platform—23 repository properties plus transaction()—and most use cases receive that entire interface regardless of how many repositories they actually need.

packages/domain/src/repositories/unit-of-work.interface.ts includes projects, environments, resources, deployments, servers, backups, notifications, monitoring, templates, tags, SSH keys, Git providers, S3, web-server settings, and more in one contract.

The testing consequence is visible in packages/usecases/src/testing/mock-unit-of-work.ts: every test fixture constructs empty {} placeholders for all unused repositories and then casts the result to IUnitOfWork. This removes compile-time guarantees precisely where tests should catch wiring mistakes.

Reviewed against master at 7f9e26400a474aa5a706d261f24fa2bf3d98ce8c.

Architectural impact

  • A feature depends structurally on every repository even when it reads only one aggregate.
  • Adding a repository expands a central interface and shared fixture across the entire application.
  • Tests can accidentally invoke a missing method on an {} placeholder at runtime.
  • Constructor signatures do not reveal a use case's true persistence needs.
  • Transaction boundaries are vague because the same service bag is also used for non-transactional reads and long-running infrastructure operations.
  • Feature extraction or modular deployment is difficult because persistence contracts are centralized by technical type rather than business capability.

Proposed direction

  1. Inject the smallest repository/query ports needed by each use case.
  2. Introduce feature-scoped repository bundles only where a workflow consistently coordinates the same aggregates, for example:
    • ProjectWorkspaceStore for project/environment/resource traversal;
    • BackupStore for schedules/runs/destinations;
    • NotificationStore for channels/deliveries.
  3. Replace transaction(work: (uow) => ...) with an application-owned transaction runner that passes a typed feature transaction context or executes a callback whose dependencies are already scoped.
  4. Separate read-model query ports from mutation repositories where this reduces multi-repository traversal.
  5. Keep Drizzle's transaction executor and repository construction inside @upstand/repositories.
  6. Migrate use cases incrementally, starting with simple one-repository CRUD operations.
  7. Delete the unsafe global mockUnitOfWork; provide typed repository fakes/builders per feature.
  8. Add a lint/architecture rule discouraging new constructors from accepting the full unit of work without a documented multi-aggregate transaction need.

Acceptance criteria

  • Ordinary use cases declare only the repositories/query ports they use.
  • Multi-aggregate transactions use a typed feature transaction context.
  • No test creates unimplemented repositories through {} casts.
  • Adding a new repository does not require updating unrelated tests or use cases.
  • Transaction duration excludes network, Docker, SSH, and other long-running external operations unless explicitly designed as a saga.
  • Repository adapters continue to share one Drizzle transaction when a workflow requires atomicity.
  • Existing project, deployment, backup, notification, and server workflows have transaction-focused integration tests.

Suggested migration order

  1. Read-only and single-repository use cases.
  2. Simple CRUD mutations.
  3. Project/environment/resource aggregate traversal.
  4. Notification and backup workflows.
  5. Deployment orchestration and other long-running workflows.
  6. Remove IUnitOfWork after the remaining explicit transaction contexts are implemented.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions