Ownership and calls replace modules
A domain owns everything in it, and it is the boundary of what other code
can see. Declarations are private to their domain unless they are marked
public.
What can be public
Entity identities, types, failures, events, queries, libraries and actions
can be public. So can a fixture, which is still callable only from tests,
fixtures and seeds: another domain's tests call a public fixture through
its domain, as planning.alice_with_todos()
(Shared test data: fixtures).
public on anything else (a function, a route group, a schedule, a
subscriber, a test or a seed) is visibility.public_not_allowed.
public on a declaration nothing outside its domain uses is
visibility.public_unused. A use counts if it comes from another domain's
code or tests, from a root tests/ scenario, or from the signature of a
public declaration one of those uses. The domain's own tests don't count.
Everything reachable from a public declaration is public. A type in a public action's, query's, library's or event's signature, and a failure it can raise, must itself be public or shared. This is checked on every compile. An internal failure is mapped or handled before it leaves the domain. A package's public surface follows the same rule (What is public).
Changing another domain's data
A public action is a domain's way for others to change its data. Checkout
in ordering can call inventory.reserve_stock(...), and both changes
commit or roll back together, in one database transaction. Another domain
never touches the entities directly. If a domain ever moves to a database
of its own, the compiler refuses the shared transaction rather than lose
the all-or-nothing guarantee.
Events are for reactions that don't need to be all-or-nothing, such as sending a confirmation email. They run after the commit, so a failed email never rolls back an order.
Calls name their owner
The part before the dot names the owner of what you call:
temporal.in_zone(x), rounding.half_even(x), Todo.get(id),
inventory.reserve_stock(...). Your own domain's actions and queries are
called by name alone: complete_todo(todo_id). An own-domain action or
query called as planning.snooze_todo() from inside planning is
semantic.own_domain_qualification.
Searching for inventory. finds the calls into that domain. It doesn't
find generated entity operations such as Order.get, which are named by
their entity.
A call names one owner. A library is called by its own name, never through
its domain, so pantry.sacks.grams_in(1) or pantry.nonexistent.thing(x)
is semantic.unknown_callee. Only a connection's operations take three
names (Calling). A call that reaches nothing
is always reported, never left to fail when it runs.
There are no module headers. Each file names where its names come from
on use lines: see Imports.
Markdown for agents: /docs/ownership-and-calls-replace-modules.md.Built from Jadpo main at a707efb.