Cross-module contracts
ACL ports, *ModuleInterface façades, and Domain Events are published contracts. Treat them like a public API — even inside a modular monolith.
Related: ACL · Events · Event delivery · Core versioning.
Thin façades (*ModuleInterface)
A provider’s outbound façade is not “every method the module has.”
| Rule | Detail |
|---|---|
| Capability-sized | Prefer WarehouseAvailabilityModuleInterface (or a focused WarehouseModuleInterface with a small surface) over one god interface |
| Stable language | Methods and DTOs speak the provider’s public vocabulary — not ORM models, not internal Entities |
| Peer ACL only | Only peer Infrastructure ACL adapters may depend on it — never peer Use Cases |
| HTTP-ready | Each method should map cleanly to a future HTTP operation |
# Prefer focused façades as the surface grows
Domain/Ports/Module/
WarehouseAvailabilityModuleInterface.php # sync reads peers need
WarehouseReservationModuleInterface.php # sync writes peers need
If you already ship a single WarehouseModuleInterface, keep it thin and split when a second capability appears — same promote spirit as Shared.
Event schema as a contract
Published Domain Events are versioned messages:
| Field | Guidance |
|---|---|
| Identity | Stable event name + explicit schemaVersion (int or semver string on the payload) |
| Correlation | eventId (unique) + correlationId / causationId when chaining |
| Payload | Primitives and small value shapes consumers need for the happy path |
| PII | Prefer codes / opaque ids over full personal data on the bus |
| Evolution | Additive fields preferred; breaking changes → new event type or major schema bump |
Rich payload (normative for happy path): publish enough for the consumer inbound Use Case to run without calling back into the publisher’s Domain.
Allowed exception (query after the fact): a separate consumer-owned sync port for corrections, admin rebuilds, or missing optional fields — never as the default “thin ID + re-fetch Domain” pattern. See Event delivery.
ACL request/response DTOs
- Consumer local port DTOs stay in the consumer language.
- Provider façade DTOs stay in the provider language.
- Mapping lives only in the ACL adapter.
- Evolve façades additively; breaking façade changes follow versioning.
Contract checklist before merge
- Façade methods are few and HTTP-mappable
- Event carries
eventId+ enough happy-path fields (+schemaVersion) - No ORM models on the wire
- Consumer Application/Domain still compile if the peer becomes HTTP
- Breaking change documented (major Core / adapter note, or new event type)
Next: transactions & failures · event delivery.