3 min readCalm pace · scan the outline anytime

Cross-module contracts

Thin façades, event schemas, ACL DTO evolution, and SemVer for Core and published contracts.

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.”

RuleDetail
Capability-sizedPrefer WarehouseAvailabilityModuleInterface (or a focused WarehouseModuleInterface with a small surface) over one god interface
Stable languageMethods and DTOs speak the provider’s public vocabulary — not ORM models, not internal Entities
Peer ACL onlyOnly peer Infrastructure ACL adapters may depend on it — never peer Use Cases
HTTP-readyEach 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:

FieldGuidance
IdentityStable event name + explicit schemaVersion (int or semver string on the payload)
CorrelationeventId (unique) + correlationId / causationId when chaining
PayloadPrimitives and small value shapes consumers need for the happy path
PIIPrefer codes / opaque ids over full personal data on the bus
EvolutionAdditive 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.

Modular Hexagonal Domain-Driven Design
Core 1.0.0-draft