The Modular Monolith is the foundation of BitzOrcas.Modern. It preserves the rapid in-process local debugging and single-deployable ergonomics of a monolith while enforcing microservice-grade physical project boundaries.
Physical Module Topology
Standard Module Structure
Every domain module is divided into 3 physical .csproj projects:
| Project | Physical Naming | Purpose | Permitted References |
|---|---|---|---|
| 1. Public Contracts | *.Contracts | Read-only DTOs and IIntegrationEvent contracts | Referenced by any external module |
| 2. Domain Core | *.Domain | Aggregate roots, entities, value objects, and domain events | Referenced only by internal Application |
| 3. Application Slices | *.Application | Vertical slices, commands, rules, and IAppModule root | Referenced only by Host composition roots |
5 Rules for Inter-Module Collaboration
- Contract-Only References: Project references across modules are strictly restricted to
*.Contractsprojects; - Queries via Read Ports: Modules query external data through public read ports, never by injecting foreign aggregate repositories;
- State Broadcasts via Integration Events: Domain mutations publish integration events via CAP Transactional Outbox;
- Transactional Outbox Consistency: Integration events commit within the same local database transaction as business state;
- In-Process Domain Events: Internal
DomainEventinstances remain scoped to the originating transaction boundary.
ArchUnit Boundary Guards
using ArchUnitNET.Domain;using ArchUnitNET.Fluent;using ArchUnitNET.Loader;using ArchUnitNET.xUnit;using Xunit;using static ArchUnitNET.Fluent.ArchRuleDefinition;
namespace BitzOrcas.Architecture.Tests;
public sealed class ModuleBoundaryTests{ // 1. Load module assemblies into ArchUnit reflection model private static readonly Architecture Architecture = new ArchLoader().LoadAssemblies( typeof(Legal.LegalModule).Assembly, typeof(Legal.Domain.MatterIntake).Assembly).Build();
[Fact] public void ExternalModules_MustNotDependOn_InternalModuleImplementations() { // 2. Enforce boundary rule: Billing must not depend on Legal.Domain IArchRule rule = Types().That() .ResideInNamespace("BitzOrcas.Modules.Billing..") .ShouldNot() .DependOnAny(Types().That().ResideInNamespace("BitzOrcas.Modules.Legal.Domain.."));
rule.Check(Architecture); }}Summary
BitzOrcas.Modern’s modular monolith combines clean boundaries with high agility, guarded by automated ArchUnit tests in CI.