Slicing Vocabulary & Rules¶
GLIMPSE uses a precise vocabulary to describe how code is organised within layers. Understanding these terms is necessary to place any new file correctly.
Vocabulary¶
- Port
-
- A delivery mechanism, named after the domain concept rather than the
- technology.
- Examples:
cli,web,db,payment_api,email - The first axis for
linksandgates, known before any code exists.
- Adapter
- The specific technology implementing a port.
- Examples:
postgres,sqlite,argparse,stripe,sendgrid -
- One port can have multiple adapters — usually coexisting, not
- interchangeable.
payment_api/stripeandpayment_api/paypalare both wired - and both live; which one handles a given payment is a business decision made
- in a mill, through the protocols it holds. Genuine substitution
- (
db/postgresvsdb/sqlite) is the rarer case: one adapter wired per - deployment, chosen in
inits. - One technology can serve multiple ports. A full-stack framework shipping both
an ORM and a request layer appears as
db/{framework}andweb/{framework}— two separate adapters that share nothing but a name.
- Kind
- The type of thing a
linksmodule holds, below the adapter. -
- Examples:
modelsandrepositoriesfor adbadapter;transport, types,signerfor an API client.- Kinds are per-adapter. The
dbshape is not a universal template.
- Examples:
- Noun
- A fat data cow — the model cluster everything else hangs off.
- Examples:
invoices,customers,proposals,users - The primary slicing axis for
pacts,mills, andspecs. -
- Which nouns exist depends on what the system is for, not on the words in
- the domain. An invoice tracker slices by
invoices; a subscription service - slices by
billing, and an invoice is a detail inside it. -
- Plurality is not prescribed; it follows the noun.
eventsare many, a panelis one.- Nouns are GLIMPSE's own axis, not a DDD import. If another axis fits a project better, slice by that instead; the layer rules don't change.
- Plurality is not prescribed; it follows the noun.
- Verb
- An activity cut inside a noun.
- Examples:
issue,refund,enroll,schedule -
- A verb module holds the records and logic of actions, not first-class data.
- Verbs nest inside nouns:
invoicesmight cut intoissueandrefund. - No catch-all verbs.
manage,organize, andmiscname no activity — if you cannot name a real one, the file is not too big yet.
- Page
- What the user touches — the slicing axis for
gates, below port and adapter. -
- Gates take whatever grouping the interface already has: a command or command
- group for a CLI, a page group and page — or a page and its subpages — for the
- web, a view for a TUI, a tool for MCP.
- Gates mirror the shape of the interface; mills mirror the domain. The two
need not line up, and forcing them to is how a sitemap ends up in
mills.
- Entity
- A persistence-level concept: the unit that a DTO and a repository wrap.
- Narrower than a noun — one noun spans many entities.
- Conceptual, not a file-layout axis.
linksslices by kind, so onemodels.pyholds many entities' models. There is nolinks/db/{adapter}/{entity}.py.
Hierarchy¶
Boundary vs core¶
Place the code before slicing it. Code that crosses a boundary is a contract
and goes to pacts; code that enforces business rules is core and goes to
mills. DTOs are the case that trips people — see boundary vs
core.
Slicing rules by layer¶
pacts, mills, specs — by noun, then verb¶
These start as single modules and become packages when they earn it. See Growing rules.
pacts.py # start here
pacts/{noun}.py # flat while the noun is small
pacts/{noun}/{verb}.py # cut by verb when the noun grows fat
mills/{noun}.py
mills/{noun}/{verb}.py
specs/{noun}.py
Each pacts module holds all boundary contracts for that noun or verb cut —
DTOs, write TypedDicts, repository protocols, errors. Split by domain concern,
never by technical kind. Contracts that belong to no noun follow the axis of
the layer they serve — pacts/{port}.py for port machinery
(TransactionProtocol), a module mirroring the inits registry for wiring
contracts (pacts/services.py — ServicesProtocol and the service protocols
it names). See the placement
algorithm.
pacts and mills share the noun/verb axis, but they are free to promote
independently — mills/ may be a package while pacts.py is still one file.
Each layer splits when its own size or friction says so.
inits — however is convenient¶
There is no axis to get right: inits is thin by construction and stays thin.
Start as a single module; when one file stops being comfortable, split it the
obvious way — a module per registry class, plus one that binds them.
Nothing rides on those names; pick what reads best. See inits.
links — port / adapter / kind¶
Packages from day one: the port is known before you write any code.
links/{port}/{adapter}.py # the whole adapter is one module
links/{port}/{adapter}/{kind}.py # once the kinds separate
links/{port}/{adapter}/{kind}/{module}.py # when a kind crosses ~1000 lines
links/{port}/{adapter}/__init__.py # facade — re-exports the public surface
Examples:
links/db/sqlite.py
links/db/postgres/models.py
links/db/postgres/repositories.py
links/payment_api/stripe.py
links/email/sendgrid.py
gates — port / adapter / page¶
Packages from day one, for the same reason. Below the adapter, gates follows
the shape of the interface itself — whatever grouping that interface already
has.
gates/{port}/{adapter}.py # flat while there is one page
gates/{port}/{adapter}/{page}.py
gates/{port}/{adapter}/{page_group}/{page}.py # whichever grouping
gates/{port}/{adapter}/{page}/{subpage}.py # the interface already has
Examples:
gates/cli/argparse.py
gates/cli/argparse/export.py # a CLI's pages are its commands
gates/cli/argparse/report/monthly.py # ...grouped as the CLI groups them
gates/web/flask/dashboard.py
gates/web/flask/checkout/payment.py # page group / page
gates/web/flask/proposal/comments.py # page / subpage
The axis is the interface, never the domain: plenty of pages belong to no single noun, and forcing one on them drags business vocabulary into the interface. A gate's job is to mirror what the user sees.