Document Stores: Postgres, Mongo & Firestore
Relevant source files
The following files were used as context for generating this wiki page:
Forze provides a unified abstraction for document persistence across multiple database backends, including Postgres, MongoDB, and Google Cloud Firestore. By separating logical definitions (DocumentSpecs) from physical infrastructure configurations, the framework allows developers to implement business logic using a consistent set of ports while swapping underlying storage engines without modifying handlers.
The architecture relies on a CQRS-style split between DocumentQueryPort for reads and DocumentCommandPort for writes. These ports are resolved at runtime via the ExecutionContext, which maps logical resource names to backend-specific adapters like MongoDocumentAdapter or PostgresDocumentAdapter.
Sources: skills/forze-documents-search/SKILL.md:1-15, skills/forze-framework-usage/SKILL.md:46-60
Architecture and Core Concepts
Logical vs. Physical Separation
A key architectural principle in Forze is the separation of the Logical Spec from the Infrastructure Config.
- DocumentSpec (Logical): Resides in the domain/application layer. It defines model types (Read, Domain, Create, Update) and a logical name. It contains no database-specific details like table or collection names.
- Integration Config (Physical): Resides in the infrastructure layer (e.g.,
PostgresDocumentConfig,MongoDocumentConfig). It maps the logical name to physical entities such as a SQL schema/table or a MongoDB database/collection.
Sources: skills/forze-domain-aggregates/SKILL.md:73-85, skills/forze-specs-infrastructure/SKILL.md:28-35
Component Relationship
The following diagram illustrates how the ExecutionContext resolves logical requests into physical database operations.
The diagram shows the flow from an application handler through the execution context to a specific database adapter.
Sources: skills/forze-framework-usage/SKILL.md:46-65, skills/forze-specs-infrastructure/SKILL.md:1-12
Postgres Document Store
The Postgres implementation uses the forze_postgres package to map document aggregates to relational tables. It supports advanced features such as schema validation at startup and history tracking via secondary relations.
Schema Validation
Forze performs strict startup validation to ensure Pydantic models align with Postgres table columns. It checks for field existence, nullability, and type compatibility (e.g., UUID fields).
Sources: src/forze_postgres/kernel/catalog/validation/validate_schema.py:125-145
Postgres Configuration Options
| Option | Type | Description |
|---|---|---|
read | tuple[str, str] | The (schema, table) for read operations. |
write | tuple[str, str] | The (schema, table) for write operations. |
history | tuple[str, str] | Optional relation for storing document revision history. |
bookkeeping_strategy | str | Strategy for managing versioning (e.g., "database"). |
conflict_target | tuple[str, ...] | Columns for ON CONFLICT logic during upserts. |
Sources: skills/forze-specs-infrastructure/SKILL.md:46-55, src/forze_postgres/kernel/catalog/validation/validate_schema.py:46-65
MongoDB Document Store
The MongoDB integration utilizes the MongoDocumentAdapter to provide high-performance document access. It leverages Mongo's native document structure while maintaining Forze's optimistic concurrency via revision (rev) fields.
Adapter Logic
The MongoDocumentAdapter coordinates between a MongoReadGateway and a MongoWriteGateway. It also integrates with a DocumentCache to provide transparent read-through caching.
Sources: tests/unit/test_forze_mongo/test_adapters_document.py:90-110
This sequence diagram shows the read-through caching logic implemented in the MongoDocumentAdapter.
Sources: tests/unit/test_forze_mongo/test_adapters_document.py:133-155, tests/unit/test_forze_mongo/test_adapters_document.py:328-345
Mutation Semantics
When performing updates, the adapter can be configured with return_new=False to optimize performance by skipping the post-write read-back and cache refresh.
Sources: tests/unit/test_forze_mongo/test_adapters_document.py:210-230
Firestore Document Store
Firestore is integrated via the forze_firestore package. Similar to other adapters, it separates read-only and read-write capabilities.
Registration
Firestore ports are registered via the FirestoreDepsModule.
ro_documents: ConfiguresFirestoreReadOnlyDocumentConfigfor query-only access.rw_documents: ConfiguresFirestoreDocumentConfigfor full command and query access.
Sources: skills/forze-documents-search/SKILL.md:58-62
Features and Model Alignment
All document stores enforce a standard set of core fields to ensure interoperability and consistent domain behavior.
Core Document Fields
| Field | Type | Purpose |
|---|---|---|
id | UUID | Primary unique identifier. |
rev | int | Revision number for optimistic concurrency. |
created_at | datetime | Timestamp of initial creation. |
last_update_at | datetime | Timestamp of the most recent mutation. |
tenant_id | UUID | Optional field for row-level/document-level isolation. |
Sources: skills/forze-domain-aggregates/SKILL.md:38-40, src/forze_postgres/kernel/catalog/validation/validate_schema.py:155-170
Multi-Tenancy
If a document is configured as tenant_aware=True, the adapters automatically derive the tenant_id from the ExecutionContext. For Postgres, this requires a NOT NULL UUID column named tenant_id. For Mongo and Firestore, the adapter ensures that queries and commands are scoped to the active tenant's identity.
Sources: src/forze_postgres/kernel/catalog/validation/validate_schema.py:155-180, tests/unit/test_forze_mongo/test_adapters_document.py:100-105
Conclusion
The Document Store system in Forze provides a robust, backend-agnostic framework for managing domain entities. By leveraging specialized adapters for Postgres, Mongo, and Firestore, Forze ensures that developers can utilize the specific strengths of each database (such as Postgres's relational integrity or Mongo's flexible document structure) while maintaining a clean, testable application architecture grounded in Hexagonal principles.