Skip to main content

Modular Monolith Boundaries Workshop

Retrieval Prompts

  1. State the three properties that distinguish a modular monolith from a monolith with folders.
  2. Define afferent coupling (Ca), efferent coupling (Ce), instability (I), and abstractness (A).
  3. Explain the "zone of pain" and "zone of uselessness" in one sentence each.
  4. Name three tools that can enforce module boundaries in code, with their language.
  5. 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):

  1. A package named shared.utils with 23 files and Ca = 18, Ce = 12.
  2. A Java service class CheckoutService with methods create_order, send_marketing_email, export_for_audit, lookup_gdpr_consent.
  3. A Python codebase where checkout/domain/pricing.py imports catalog.persistence.product_repo.
  4. A Ruby Packwerk config with enforce_dependencies: false for a package that imports 9 others.
  5. An ArchUnit rule that checks nothing because the should().onlyDependOnClassesThat() clause is missing.
  6. 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:

  1. "Module X is the only place Y is allowed to be imported from."
  2. "No cycles between module X and module Y."
  3. "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.