Scaffold a service app¶
The package ships a startserviceapp management command — a subclass
of Django's startapp that lays out an app the way this library
encourages.
Setup¶
Add the package to INSTALLED_APPS to make the command discoverable:
Then:
What you get¶
billing/
├── __init__.py
├── apps.py
├── admin.py
├── urls.py
├── models/__init__.py
├── views/__init__.py
├── services/__init__.py
├── selectors/__init__.py
├── validators/__init__.py
├── serializers/__init__.py
├── utils/__init__.py
├── migrations/__init__.py
└── tests/__init__.py
Differences vs. plain startapp:
models/andviews/are packages, not single files. The single- file shape is a poor fit when each model and each view is its own unit of work.services/,selectors/,validators/,serializers/,utils/are added as packages alongside.- An empty
urls.pyis included —startappdoesn't ship one.
A note on validators/¶
The validators/ package is a stylistic convention, not a library
feature. The library doesn't import from it or look it up by name. It
exists to give business-level validation a home of its own — rules like
"a draft invoice can only be sent if the customer has a verified email"
or "refunds beyond 30 days require manager approval". These belong
neither in the model nor in the serializer.
The split this layout suggests:
| Concern | Lives in |
|---|---|
| Type validation, required fields, format checks | DRF serializers (serializers/), including DataclassSerializer for service inputs |
| Business rules / cross-record invariants / external-state checks | Functions in validators/, called from services |
| Side effects, persistence, orchestration | services/ |
Use it, ignore it, or rename it¶
The scaffolding is a starting point, not a contract. Nothing in the
library cares whether your service lives in myapp/services/foo.py or
myapp/foo_service.py. Pick the layout that makes your codebase easier
to navigate; the convention exists because there's value in a project
where every app looks the same.