Modular Monolith Boundaries Workshop
Retrieval Prompts
- State the three properties that distinguish a modular monolith from a monolith with folders.
- Define afferent coupling (Ca), efferent coupling (Ce), instability (I), and abstractness (A).
- Explain the "zone of pain" and "zone of uselessness" in one sentence each.
- Name three tools that can enforce module boundaries in code, with their language.
- State the minimum 4-rule fitness-function set for a modular monolith.
Compare and Distinguish
- functional cohesion vs coincidental cohesion -- give a code-level example of each.
- afferent vs efferent coupling -- which is "incoming"? Which rises when a module takes on more dependencies?
- Packwerk enforce_dependencies vs enforce_privacy -- different concerns; illustrate with an example rule.
- closed layer vs enforced module boundary -- where does each live (runtime? CI?); what does each buy you?
Common Mistake Check
Identify the issue in each code/structure fragment (2-3 sentences each):
- A package named
shared.utilswith 23 files and Ca = 18, Ce = 12. - A Java service class
CheckoutServicewith methodscreate_order,send_marketing_email,export_for_audit,lookup_gdpr_consent. - A Python codebase where
checkout/domain/pricing.pyimportscatalog.persistence.product_repo. - A Ruby Packwerk config with
enforce_dependencies: falsefor a package that imports 9 others. - An ArchUnit rule that checks nothing because the
should().onlyDependOnClassesThat()clause is missing. - A "modular monolith" whose CI has no architecture test; boundaries are documented in a wiki page only.
Mini Application
Pick a real codebase (work, open source project, a class project). Spend 60-90 minutes doing the following:
Step 1 -- Module graph (15 min)
Draw a mermaid diagram of the top-level modules or packages. Aim for 6-12 nodes. Label each with one sentence: "owns X."
Step 2 -- Coupling audit (20 min)
For each module, by inspection of imports:
- Ca (how many modules import from this one)
- Ce (how many modules this one imports from)
- I = Ce / (Ca + Ce)
- A = rough ratio of abstract classes / interfaces (eyeball it)
Produce a table. Flag any module with I and A both > 0.5 or both < 0.5 -- those are on the main sequence violators.
Step 3 -- Boundary rule set (15 min)
Pick the enforcement tool matching your language. Write 3 rules:
- "Module X is the only place Y is allowed to be imported from."
- "No cycles between module X and module Y."
- "Cross-module access must go through
api/packages."
Produce the exact syntax. Do not leave pseudocode.
Step 4 -- Run and triage (20 min)
Run the rules. Count violations. Produce a 3-bucket plan:
- fix now (less than 1 hour each)
- grandfather (add to ignore list with TODO and ticket)
- rule is wrong (refine rule)
Step 5 -- Write-up (10 min)
One page summarizing: module list, worst coupling offender, the three rules you would commit to CI, and the grandfathered backlog.
Evidence Check
- Your diagram has 6-12 modules labeled with responsibility.
- Your Ca/Ce table identifies at least one candidate refactor (zone of pain or uselessness).
- Your 3 rules are in the actual tool syntax, not pseudocode.
- You have run them and have a real violation count.
- You have a written grandfathering plan for anything you cannot fix today.
Page complete only if someone else could read your write-up and commit the same rules to the same codebase.