A small Carrier service can be understood from a few files. An enterprise Carrier estate may contain many services, domains, modules, policies, integrations, and generated artifacts. At that scale, architecture depends less on syntax and more on disciplined boundaries.
Bounded Contexts
A bounded context is a domain area with its own language, data ownership, rules, and lifecycle. Carrier services should usually map to bounded contexts or to clear slices within them. The boundary should reflect business responsibility, not just technical convenience. When one context needs information from another, it should integrate through a defined API, event, or shared module — not by silently taking over the other context's data model.
Modular Monoliths vs Distributed Services
Enterprise teams often rush from monolith to microservices before the domain boundaries are stable. That can make the architecture more complex without making it more understandable. Carrier supports both styles. The better starting question is not how many deployables? but where are the ownership boundaries?
Shared Modules
Reusable local modules belong under modules/<org>/<name>/src/. Good candidates include common request or response types, shared form or questionnaire definitions, reusable validation helpers, organization-wide value objects, and cross-service integration contracts. Poor candidates include unstable domain internals, service-specific persistence models, or business logic that belongs to a single owning service. A shared module should have a clear owner. Otherwise, it becomes a place where teams put code they do not know how to classify.
Avoiding Coupling Through Imports
Imports are architectural dependencies. Architects should watch for warning signs: a domain importing many unrelated domains; route files importing persistence internals from another context; shared modules depending on application-specific services; a low-level module importing a high-level workflow; a service importing another service's private model definitions instead of using a contract. The goal is not to eliminate dependencies — it is to make dependency direction intentional.