Files
docs/standards_src/wiki/05_crud_template.md

2.1 KiB

CRUD Template

Summary

Every resource follows the same four-file slice. Repetition is intentional: it makes the codebase predictable and generatable.


The slice

domain/<resource>/
    models.py               # pure Pydantic model (no DB fields)
    ports/
        db.py               # document model + repository factory
        service.py          # service class + factory
        converters.py       # (optional) transforms

models.py

A framework-free Pydantic model describing the business shape.

class Item(BaseModel):
    name: str
    quantity: int = 0

ports/db.py

The persistence model extends the business model and adds database concerns. The repository factory selects the right repository flavour.

class ItemDocument(Item, BaseDocument):   # BaseDocument adds id/created_at/...
    pass

def create_item_repo(*, owner_repo=None) -> ItemRepository:
    return repo_factory(
        collection="items",
        document=ItemDocument,
        populate_rules=[PopulateRule(field="owner", collection="users")],
        cache=None,               # or a CachedBaseRepository config
    )

ports/service.py

The service is built through the DI library, injecting the repository. The generic CRUD base provides create/read/update/patch/delete/search/....

class ItemService(CrudsServiceAdaptor[Item, ItemDocument]):
    model = Item
    schema = ItemDocument

def get_item_service(repo) -> ItemService:
    return build(ItemService, instances={"repo": repo})

Naming conventions

Concept Convention
collection plural snake case (items)
document <Resource>Document
repository factory create_<resource>_repo
service <Resource>Service
service factory get_<resource>_service
resource key <resource>_svc
operationId list_items, create_item, ...

Optional files

  • converters.py — pure functions mapping between domain types.
  • ports/enrichment.py — adapts a pipeline into an async callable the service can invoke (used when a resource is populated by a pipeline).