modular-monolith
Skill15 starsChanged 43 days agoMITView on GitHub
Expert Modular Monolith architecture assistance covering bounded module boundaries, internal public APIs, decoupled package structures, and single-deployment velocity. Use when scaling monolith development teams, preventing spaghetti code, or preparing for future microservice extraction.
In this repository350 other files it gives its agents.
- airflowSkill
- catboostSkill
- claudeSkill
- comfyuiSkill
- cursor-aiSkill
- dalleSkill
- daskSkill
- dbtSkill
- deepseekSkill
- fastaiSkill
- geminiSkill
- github-copilotSkill
- huggingfaceSkill
- jaxSkill
- jupyterSkill
- kerasSkill
- langchainSkill
- lightgbmSkill
- llamaindexSkill
- llamaSkill
- matplotlibSkill
- midjourneySkill
- mistralSkill
- mlflowSkill
- nltkSkill
- numpySkill
- ollamaSkill
- openai-gptSkill
- opencvSkill
- pandasSkill
- perplexitySkill
- plotlySkill
- polarsSkill
- pytorchSkill
- raySkill
- scikit-learnSkill
- seabornSkill
- spacySkill
- sparkSkill
- stable-diffusionSkill
- tensorflowSkill
- vaexSkill
- weights-biasesSkill
- whisperSkill
- xgboostSkill
- api-gatewaySkill
- clean-architectureSkill
- cqrsSkill
- domain-driven-designSkill
- event-drivenSkill
- event-sourcingSkill
- graphqlSkill
- grpcSkill
- hexagonalSkill
- microservicesSkill
- monolithSkill
- rest-apiSkill
- sagaSkill
- serverlessSkill
- service-meshSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 137
Keep Deployment Pipeline Unified: Enjoy the speed of atomic single-binary deployments and coordinated database migrations.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Modular Monolith
A Modular Monolith structures a single deployment artifact into discrete, loosely-coupled domain modules with strict encapsulation boundaries, balancing microservice-level separation with monolithic operational simplicity.
When to Use
- Medium-to-Large Engineering Teams: Scaling a codebase across multiple teams without the operational overhead of microservices.
- Single-Deployment Velocity: Retaining simple CI/CD pipelines, single-transaction database migrations, and local Docker setups.
- Clear Domain Boundaries: Preventing spaghetti code by enforcing strict module encapsulation and public API boundaries at compile-time.
- Pre-Microservice Preparation: Architecting systems that can be cleanly sliced into separate microservices later if scaling demands it.
Quick Start
/src
/Modules
/Catalog <-- Public Interface defined here
/Core <-- Internals (Classes, Logic) hidden
/API <-- Public Contracts (DTOs)
/Ordering
/Core
/Shipping
/Core
/Shared <-- Infrastructure, Event Bus// Communication via In-Process Interfaces or Events
public class OrderService {
private readonly ICatalogModule _catalog; // In-memory reference, but strict contract
public async Task Checkout(string productId) {
var product = await _catalog.GetProduct(productId); // Fast 0ms call
// ...
}
}Core Concepts
Strict Module Encapsulation & Public API Facades
Modules interact exclusively through explicit public facades; internal repositories, models, and helpers are unexported:
src/modules/
├── billing/
│ ├── internal/ # Private tables, services, entities
│ └── index.ts # Public Interface & Facade ONLY
└── shipping/
├── internal/
└── index.tsIn-Process Domain Events
Modules communicate asynchronously across boundaries using in-memory event buses:
// modules/common/event-bus.ts
import EventEmitter from "events";
export const inProcessBus = new EventEmitter();
// modules/orders/internal/order.service.ts
inProcessBus.emit("order.created", { orderId: "123", customerId: "456" });
// modules/notifications/internal/notification.listener.ts
inProcessBus.on("order.created", (payload) => {
// Executes in same process, decoupled from order module code
});Architecture Linter Boundary Enforcement
Enforces boundary rules in CI using tools like eslint-plugin-boundaries or ArchUnit:
// .eslintrc.js
rules: {
"boundaries/element-types": [2, {
default: "disallow",
rules: [
{ from: "module:billing", allow: ["module:billing", "module:common"] },
{ from: "module:shipping", allow: ["module:shipping", "module:common"] },
]
}]
}Architectural Advantages over Microservices
- Zero-Latency In-Memory Invocation: Module boundaries communicate through typed facades without network serialization or RPC latency.
- Local Transactional Consistency: ACID transactions remain possible across co-located tables without distributed 2PC or complex Saga orchestration.
- Unified Toolchain Refactoring: IDE refactorings, cross-cutting interface changes, and atomic commits span the full system without multi-repo drift.
Common Patterns
Internal Module Public API Facade
Problem: Modules access each other's private tables, destroying encapsulation and preventing future service extraction.
Solution: Expose an explicit Public API interface per module.
// modules/billing/index.ts (Billing Module Public Boundary)
export interface BillingModuleApi {
chargeCustomer(
customerId: string,
amountCents: number,
): Promise<PaymentReceipt>;
getSubscriptionStatus(customerId: string): Promise<SubscriptionStatus>;
}
// Internal billing tables, services, and repositories are NOT exported.
export class BillingFacade implements BillingModuleApi {
constructor(private readonly paymentService: InternalPaymentService) {}
async chargeCustomer(id: string, amount: number) {
return this.paymentService.processCharge(id, amount);
}
async getSubscriptionStatus(id: string) {
return this.paymentService.checkSubscription(id);
}
}Best Practices
Do:
- Enforce Boundaries with Build Tools: Use ESLint rules, TypeScript project references, or ArchUnit to prevent illegal module cross-imports.
- Isolate Module Schemas: Use separate database schemas (
billing.*,users.*) within the same database to prevent illicit SQL joins. - Communicate Across Modules via Facades or Events: Disallow direct access to another module's internal classes or database repositories.
- Keep Deployment Pipeline Unified: Enjoy the speed of atomic single-binary deployments and coordinated database migrations.
Don't:
- Execute direct cross-module foreign key joins: Reference entities from foreign modules by ID only; do not write
LEFT JOIN shipping.parcels. - Share mutable memory state across modules: Pass immutable DTOs or primitive values through public facade methods.
- Prematurely split into microservices: Extract a module into a microservice only when deployment frequency or scaling demands it.
Troubleshooting
| Error | Cause | Solution |
Cyclic module dependency | Modules directly importing internal classes across boundaries. | Expose strictly typed public APIs/facades and enforce boundary linting. |
Accidental shared database table | Cross-module queries joining tables across domains. | Isolate schemas per module or access data exclusively through module interfaces. |
Leaky module abstractions | Direct access to internal database entities instead of DTOs. | Return immutable Data Transfer Objects (DTOs) from module public services. |
References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

