ЁЯПл The SchoolтА║ЁЯПЫя╕П Software ArchitectureтА║ЁЯПШя╕П рдзрдбрд╛ 06 тАФ рд╢реИрд▓реА: рдПрдХ рдЗрдорд╛рд░рдд, рднрд┐рдВрддреА рдЕрд╕рд▓реЗрд▓реА рдПрдХ рдЗрдорд╛рд░рдд, рдХреА рдХреЕрдореНрдкрд╕
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯПШя╕П рдзрдбрд╛ 06 тАФ рд╢реИрд▓реА: рдПрдХ рдЗрдорд╛рд░рдд, рднрд┐рдВрддреА рдЕрд╕рд▓реЗрд▓реА рдПрдХ рдЗрдорд╛рд░рдд, рдХреА рдХреЕрдореНрдкрд╕

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 06 ┬╖ рдорд╛рдЧреЗ: lesson-05-ddd ┬╖ рдкреБрдвреЗ: lesson-07-events-cqrs


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ05, рдЖрдгрд┐ рд╕рдЧрд│реНрдпрд╛рдд рдореЛрдард╛ рд░рдЪрдирд╛рддреНрдордХ рдирд┐рд░реНрдгрдп: рдХрд┐рддреА deployable units? рдПрдХ monolith, рдПрдХ modular monolith, рдХреА microservices тАФ рдкреНрд░рддреНрдпреЗрдХрд╛рдЪреНрдпрд╛ рд╢реЗрдЬрд╛рд░реА рддреНрдпрд╛рдЪреА рдХрд┐рдВрдордд рд▓рд┐рд╣реВрди: network hops, partial failure, рдЕрдиреЗрдХ services рдордзреНрдпреЗ рдкрд╕рд░рдгрд╛рд░реЗ transactions, рдЖрдгрд┐ рд╕реАрдорд╛ рдХреЛрдг рд▓рд╛рдЧреВ рдХрд░рддреЗ. arch/models.py рдордзреАрд▓ styles() рдЖрдгрд┐ arch/demo.py рдордзреАрд▓ styles. System Design рд╢рд╛рд│реЗрдЪрд╛ monolith vs microservices рд╡рд░рдЪрд╛ рдзрдбрд╛ рд╣рд╛рдЪ рдирд┐рд░реНрдгрдп traffic рдЪреНрдпрд╛ рдмрд╛рдЬреВрдиреЗ рдкрд╛рд╣рддреЛ; рдЗрдереЗ рдЖрдкрдг code рдЪреА рд░рдЪрдирд╛ рдкрд╛рд╣рддреЛ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдХрддрд░рд┐рдирд╛ рдЪрд╛рд░ рд╡рд┐рднрд╛рдЧ рддреАрди рдкреНрд░рдХрд╛рд░реЗ рдЪрд╛рд▓рд╡реВ рд╢рдХрддреЗ. ЁЯПл

  1. рдПрдХ рдореЛрдард╛ рд╣реЙрд▓. рд╕рдЧрд│реЗ рдПрдХрд╛рдЪ рдЦреЛрд▓реАрдд. рдмреЛрд▓рдгреЗ рд╕реЛрдкреЗ тАФ рдлрдХреНрдд рдорд╛рдЧреЗ рд╡рд│рд╛. рдкрдг рдХреЛрдгреАрд╣реА рдХреЛрдгрд╛рдЪреЗрд╣реА рдХрд╛рдЧрдж рдЙрдЪрд▓реВ рд╢рдХрддреЗ, рдЖрдгрд┐ рд╡рд░реНрд╖рднрд░рд╛рдиреЗ рдХреЛрдгрддреЗ рдХрд╛рдЧрдж рдХреЛрдгрд╛рдЪреЗ рд╣реЗ рдХреЛрдгрд╛рд▓рд╛рдЪ рдорд╛рд╣реАрдд рдирд╕рддреЗ.
  2. рднрд┐рдВрддреА рдЖрдгрд┐ рджрд╛рд░реЗ рдЕрд╕рд▓реЗрд▓реА рдПрдХ рдЗрдорд╛рд░рдд. рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рднрд╛рдЧрд╛рдЪреЗ рд╕реНрд╡рддрдГрдЪреЗ office. рдлрдХреНрдд office рдЪреНрдпрд╛ рдЦрд┐рдбрдХреАрддреВрдирдЪ рдмреЛрд▓рддрд╛ рдпреЗрддреЗ. рддрд░реАрд╣реА рдПрдХ рдЗрдорд╛рд░рдд, рдПрдХ рдХрд┐рд▓реНрд▓реА, рдПрдХ fire alarm тАФ рдкрдг рдкреНрд░рддреНрдпреЗрдХ office рдЖрдкрд▓реЗ рдХрд╛рдЧрдж рд╕реНрд╡рддрдГ рд╕рд╛рдВрднрд╛рд│рддреЗ.
  3. рдХреЕрдореНрдкрд╕рднрд░ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рдЗрдорд╛рд░рддреА. рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рднрд╛рдЧ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рдЗрдорд╛рд░рддреАрдд. рдкреНрд░рддреНрдпреЗрдХ рдЬрдг рдЖрдкрд▓реНрдпрд╛рдЪ рджрд┐рд╡рд╢реА рд░рдВрдЧ рджреЗрдК рд╢рдХрддреЛ, рдкреБрдиреНрд╣рд╛ рдмрд╛рдВрдзреВ рд╢рдХрддреЛ рдХрд┐рдВрд╡рд╛ рдмрдВрдж рдХрд░реВ рд╢рдХрддреЛ. рдкрдг рдЖрддрд╛ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрди рдореНрд╣рдгрдЬреЗ рдХреЕрдореНрдкрд╕рднрд░ рдЪрд╛рд▓рдд рдЬрд╛рдгреЗ тАФ рдЬрд╛рд╕реНрдд рд╡реЗрд│ рд▓рд╛рдЧрддреЛ, рдЖрдгрд┐ рдХрдзреА рдкрд╛рдКрд╕ рдкрдбрддреЛ рдЖрдгрд┐ рдирд┐рд░реЛрдкреНрдпрд╛ рдкреЛрд╣реЛрдЪрддрдЪ рдирд╛рд╣реА.

рдмрд╣реБрддреЗрдХ рд╢рд╛рд│рд╛рдВрдиреА 2 рдкрд╛рд╕реВрди рд╕реБрд░реБрд╡рд╛рдд рдХрд░рд╛рд╡реА. рдПрдЦрд╛рджреНрдпрд╛ рд╡рд┐рднрд╛рдЧрд╛рд▓рд╛ рдкреБрдиреНрд╣рд╛ рдмрд╛рдВрдзрдгреНрдпрд╛рд╕рд╛рдареА рдЦрд░реЛрдЦрд░рдЪ рд╕реНрд╡рддрдГрдЪрд╛ рджрд┐рд╡рд╕ рд╣рд╡рд╛ рдЕрд╕реЗрд▓, рддреЗрд╡реНрд╣рд╛рдЪ рддреНрдпрд╛рд▓рд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рдЗрдорд╛рд░рддреАрдд рд╣рд▓рд╡рд╛.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    subgraph mono["ЁЯПЪя╕П monolith тАФ 1 unit"]
      m1["admissions ┬╖ grades ┬╖ fees ┬╖ timetable<br/>anyone imports anyone"]
    end
    subgraph modmono["ЁЯПл modular monolith тАФ 1 unit"]
      a["admissions"] --- g["grades"] --- f["fees"] --- t["timetable"]
    end
    subgraph micro["ЁЯПШя╕П microservices тАФ 4 units"]
      sa["admissions svc"]
      sg["grades svc"]
      sf["fees svc"]
      st["timetable svc"]
      sg -->|"hop"| sa
      sa -->|"hop"| st
    end
    note["report card: 0 hops ┬╖ 0 hops ┬╖ 2 hops (+2 ms, 99.8%)"]

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/software-architecture/lesson-diagrams.html#l06

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рд╣рд╛ рд╕рдЧрд│реНрдпрд╛рдд рдореЛрдард╛ blast radius рдЕрд╕рд▓реЗрд▓рд╛ рдирд┐рд░реНрдгрдп рдЖрд╣реЗ (рдзрдбрд╛ 01), рдЖрдгрд┐ рдЗрддрд░ рдХреЛрдгрддреНрдпрд╛рд╣реА рдирд┐рд░реНрдгрдпрд╛рдкреЗрдХреНрд╖рд╛ рддреЛ рдЬрд╛рд╕реНрдд рд╡реЗрд│рд╛ рдлрд╛рд░ рд▓рд╡рдХрд░ рдШреЗрддрд▓рд╛ рдЬрд╛рддреЛ. Microservices coupling рдХрд╛рдвреВрди рдЯрд╛рдХрдд рдирд╛рд╣реАрдд тАФ рддреЗ рддреНрдпрд╛рд▓рд╛ network рд╡рд░ рд╣рд▓рд╡рддрд╛рдд, рдЬрд┐рдереЗ рддреЗ рдЬрд╛рд╕реНрдд рд╣рд│реВ рдЕрд╕рддреЗ, рдЕрд░реНрдзреНрдпрд╛рддрдЪ fail рд╣реЛрдК рд╢рдХрддреЗ, рдЖрдгрд┐ рджрд┐рд╕рд╛рдпрд▓рд╛ рдЕрд╡рдШрдб рдЕрд╕рддреЗ. Modular monolith рд╕реАрдорд╛рдВрдЪреЗ рдмрд╣реБрддреЗрдХ рдлрд╛рдпрджреЗ рдЖрдзреА рдорд┐рд│рд╡рддреЛ, рдЖрдгрд┐ рджрд╛рд░ рдЙрдШрдбреЗ рдареЗрд╡рддреЛ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

arch/models.py рдордзреАрд▓ TRACES рддреАрди requests рдХреЛрдгрддреНрдпрд╛ рд╡рд┐рднрд╛рдЧрд╛рдВрддреВрди рдЬрд╛рддрд╛рдд рддреЗ рд╕реВрдЪреАрдмрджреНрдз рдХрд░рддреЛ (report card grades тЖТ admissions тЖТ timetable рд▓рд╛ рд╕реНрдкрд░реНрд╢ рдХрд░рддреЗ). styles(traces, departments, hop_ms, hop_fail) рдкреНрд░рддреНрдпреЗрдХ рд╢реИрд▓реАрд╕рд╛рдареА рдореЛрдЬрддреЛ: network hops (process рдЪреА рд╕реАрдорд╛ рдУрд▓рд╛рдВрдбрдгрд╛рд░рд╛ рджреЛрди рд╡рд┐рднрд╛рдЧрд╛рдВрдордзрд▓рд╛ call), рд╡рд╛рдврд▓реЗрд▓реА latency (hops ├Ч hop_ms) рдЖрдгрд┐ рдпрд╢рд╛рдЪреА рд╢рдХреНрдпрддрд╛ ((1 тИТ hop_fail) ^ hops). Defaults тАФ рдкреНрд░рддреНрдпреЗрдХ hop рд▓рд╛ 1 ms рдЖрдгрд┐ 1000 рдкреИрдХреА 1 failure тАФ рдпрд╛ рд╢рд┐рдХрд╡рдгреАрд╕рд╛рдареАрдЪреНрдпрд╛ рдЧреГрд╣реАрддрдХреЗ рдЖрд╣реЗрдд, рдореЛрдЬрдорд╛рдк рдирд╡реНрд╣реЗ; snippet рддреНрдпрд╛ рдмрджрд▓рддреЛ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 arch/demo.py styles
python3 - <<'EOF'
import sys; sys.path.insert(0, "arch"); from models import styles
for ms, fail in ((1, 0.001), (5, 0.01)):
    r = styles(hop_ms=ms, hop_fail=fail)[2]["per"]["enrol"]
    print(f"microservices, {ms} ms and {fail:.1%} failure per hop тЖТ enrol: {r[0]} hops, +{r[1]:.0f} ms, succeeds {r[2]:.2%}")
chatty = {"report card": ["web"] + ["grades", "admissions"] * 5}
r = styles(chatty, hop_ms=1, hop_fail=0.001)[2]["per"]["report card"]
print(f"a chatty report card with {r[0]} hops тЖТ +{r[1]:.0f} ms, succeeds {r[2]:.2%}")
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

styles рд╣реЗ рдЫрд╛рдкрддреЛ:

тФАтФА the same 4 departments in three styles (teaching assumptions: 1 ms and 1-in-1000 failure per network hop)
   monolith         1 deployable unit(s) ┬╖ own deploys: no  ┬╖ boundaries enforced by nothing (any room may import any room)
   modular monolith 1 deployable unit(s) ┬╖ own deploys: no  ┬╖ boundaries enforced by a build-time check (lesson 09)
   microservices    4 deployable unit(s) ┬╖ own deploys: yes ┬╖ boundaries enforced by the network (separate processes)
                    report card 2 hop(s) +2 ms 99.8% ┬╖ pay fees 1 hop(s) +1 ms 99.9% ┬╖ enrol 3 hop(s) +3 ms 99.7% ┬╖ enrol needs a saga across services

рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:

microservices, 1 ms and 0.1% failure per hop тЖТ enrol: 3 hops, +3 ms, succeeds 99.70%
microservices, 5 ms and 1.0% failure per hop тЖТ enrol: 3 hops, +15 ms, succeeds 97.03%
a chatty report card with 10 hops тЖТ +10 ms, succeeds 99.00%

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рджреЛрдиреНрд╣реА monolith рд╢реИрд▓реАрдВрдордзреНрдпреЗ 0 hops рдЕрд╕рддрд╛рдд: рд╡рд┐рднрд╛рдЧрд╛рдВрдордзрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ call рдореНрд╣рдгрдЬреЗ function call, рдЖрдгрд┐ рдкреНрд░рд╡реЗрд╢ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореНрд╣рдгрдЬреЗ рдПрдХрдЪ database transaction. Microservices рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рднрд╛рдЧрд╛рд▓рд╛ рд╕реНрд╡рддрдГрдЪрд╛ deploy рджреЗрддрд╛рдд тАФ рдЗрддрд░ рд╢реИрд▓реА рджреЗрдК рд╢рдХрдд рдирд╛рд╣реАрдд рддреА рдПрдХрдореЗрд╡ рдЧреЛрд╖реНрдЯ тАФ рдЖрдгрд┐ рддреНрдпрд╛рдЪреА рдХрд┐рдВрдордд рдкреНрд░рддреНрдпреЗрдХ request рд╡рд░ рдореЛрдЬрддрд╛рдд: enrol 3 hops рдУрд▓рд╛рдВрдбрддреЛ рдЖрдгрд┐ рдПрдХ saga рдмрдирддреЛ. рдЬрд╛рд╕реНрдд рд╡рд╛рдИрдЯ (рдкрдг рдЧрд░реНрджреАрдЪреНрдпрд╛ network рд╕рд╛рдареА рд╡рд╛рд╕реНрддрд╡рд╡рд╛рджреА) рдЖрдХрдбреНрдпрд╛рдВрд╕рд╣, рдкреНрд░рддреНрдпреЗрдХ hop рд▓рд╛ 5 ms рдЖрдгрд┐ 1%, retries, timeouts рдЖрдгрд┐ рднрд░рдкрд╛рдИ рдЬреЛрдбрд▓реНрдпрд╛рд╢рд┐рд╡рд╛рдп 100 рдкреИрдХреА рд╕реБрдорд╛рд░реЗ 3 рдкреНрд░рд╡реЗрд╢ рдордзреЗрдЪ рдХреБрдареЗрддрд░реА fail рд╣реЛрддрд╛рдд (97.03% рдпрд╢рд╕реНрд╡реА). рдЖрдгрд┐ рдПрдХ chatty design тАФ рдПрдХрд╛ call рдРрд╡рдЬреА 10 рдЫреЛрдЯреЗ calls тАФ рджреЛрдиреНрд╣реА рдХрд┐рдорддреА рдЧреБрдгрд╛рдХрд╛рд░рд╛рдиреЗ рд╡рд╛рдврд╡рддреЛ. Network рдЪрд╛ рдХрд░ рди рднрд░рддрд╛ рдЬреНрдпрд╛рдЪреНрдпрд╛ рд╕реАрдорд╛ рддрдкрд╛рд╕рд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд рдЕрд╢реА modular monolith рд╣реА рдПрдХрдореЗрд╡ рд╢реИрд▓реА рдЖрд╣реЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

On a real project тАФ modular monolith рд▓рд╛ рдПрдХ рд╕реАрдорд╛-рддрдкрд╛рд╕рдгрд╛рд░рд╛ (boundary checker) рд▓рд╛рдЧрддреЛ, рдирд╛рд╣реАрддрд░ рд╡рд░реНрд╖рднрд░рд╛рдд рддреЛ рдПрдХрд╛ рд╕рд╛рдзреНрдпрд╛ monolith рдордзреНрдпреЗ рдврд╛рд╕рд│рддреЛ:

# packs/grades/package.yml
enforce_dependencies: true
dependencies:
  - packs/admissions
bin/packwerk check      # fails on a reference to a package not listed in dependencies
@Test
void departmentsAreModules() {
    ApplicationModules.of(SchoolApplication.class).verify();
}

рдЙрд▓рдЯ рджрд┐рд╢реЗрдиреЗ рдЬрд╛рдгреНрдпрд╛рдЪреЗ рдПрдХ рдЦреВрдк рдЪрд░реНрдЪрд┐рд▓реЗ рдЧреЗрд▓реЗрд▓реЗ рдЙрджрд╛рд╣рд░рдг: 2023 рдордзреНрдпреЗ Amazon Prime Video рдЯреАрдордиреЗ рд▓рд┐рд╣рд┐рд▓реЗ рдХреА рдПрдХ audio/video monitoring service рд╡рд┐рдЦреБрд░рд▓реЗрд▓реНрдпрд╛ serverless components рдордзреВрди рдПрдХрд╛рдЪ process рдордзреНрдпреЗ рд╣рд▓рд╡рд▓реНрдпрд╛рд╡рд░ рддреНрдпрд╛ service рдЪрд╛ infrastructure рдЦрд░реНрдЪ 90% рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдХрдореА рдЭрд╛рд▓рд╛. рдзрдбрд╛ "рдХрдзреАрдЪ distribute рдХрд░реВ рдирдХрд╛" рдЕрд╕рд╛ рдирд╛рд╣реА тАФ рддреЛ "рдЬрд┐рдереЗ рдлрд╛рдпрджрд╛ hop рдЪреА рдХрд┐рдВрдордд рднрд░реВрди рдХрд╛рдврддреЛ рддрд┐рдереЗрдЪ distribute рдХрд░рд╛" рдЕрд╕рд╛ рдЖрд╣реЗ.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдПрдЦрд╛рджреА service рд╡реЗрдЧрд│реА рдХрд╛рдврдгреНрдпрд╛рдЖрдзреА, рддреА рдХреЛрдгрддреЗ characteristic рджреЗрддреЗ (рд╕реНрд╡рддрдВрддреНрд░ deploy? рд╕реНрд╡рддрдВрддреНрд░ scaling? рд╡реЗрдЧрд│реА рдЯреАрдо?) рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ request рдорд╛рдЧреЗ hops рдордзреНрдпреЗ рддрд┐рдЪреА рдХрд┐рдВрдордд рдХрд┐рддреА рд╣реЗ рд▓рд┐рд╣реВрди рдареЗрд╡рд╛. рдкрд╣рд┐рд▓реЗ рд╕рд╛рдВрдЧрддрд╛ рдпреЗрдд рдирд╕реЗрд▓, рддрд░ module рддрд╕реЗрдЪ рдареЗрд╡рд╛.

тПня╕П рдкреБрдвреЗ

рдПрдХрд╛рдЪ рдЗрдорд╛рд░рддреАрддрд╣реА рд╡рд┐рднрд╛рдЧ рдПрдХрдореЗрдХрд╛рдВрдирд╛ call рдХрд░рдгреЗ рдерд╛рдВрдмрд╡реВрди рд╕реВрдЪрдирд╛ рд▓рд╛рд╡реВ рд╢рдХрддрд╛рдд. Event-driven architecture рдЖрдгрд┐ CQRS.

git checkout lesson-07-events-cqrs

ЁЯПШя╕П Lesson 06 тАФ Styles: one building, one building with walls, or a campus

ЁЯУН You are here: Lesson 06 of 12 ┬╖ Previous: lesson-05-ddd ┬╖ Next: lesson-07-events-cqrs


ЁЯУж What's in this branch

Lessons 01тАУ05, plus the biggest structural choice: how many deployable units? A monolith, a modular monolith, or microservices тАФ with the costs written next to each: network hops, partial failure, transactions that span services, and who enforces the boundaries. styles() in arch/models.py and styles in arch/demo.py. The System Design school's lesson on monolith vs microservices looks at the same choice from the traffic side; here we look at the code structure.

ЁЯзТ Explain like I'm 5

Katrina can run the four departments in three ways. ЁЯПл

  1. One big hall. Everyone in one room. Easy to talk тАФ just turn around. But anyone can pick up anyone's papers, and after a year nobody knows whose papers are whose.
  2. One building, with walls and doors. Each department has its own office. You can only talk through the office window. Still one building, one key, one fire alarm тАФ but each office keeps its own papers.
  3. Separate buildings across the campus. Each department in its own building. Each can repaint, rebuild or close on its own day. But now every question is a walk across the campus тАФ slower, and sometimes it rains and the messenger does not arrive.

Most schools should start with 2. Move a department into its own building only when it really needs its own day for rebuilding.

ЁЯЧ║я╕П Diagram

flowchart LR
    subgraph mono["ЁЯПЪя╕П monolith тАФ 1 unit"]
      m1["admissions ┬╖ grades ┬╖ fees ┬╖ timetable<br/>anyone imports anyone"]
    end
    subgraph modmono["ЁЯПл modular monolith тАФ 1 unit"]
      a["admissions"] --- g["grades"] --- f["fees"] --- t["timetable"]
    end
    subgraph micro["ЁЯПШя╕П microservices тАФ 4 units"]
      sa["admissions svc"]
      sg["grades svc"]
      sf["fees svc"]
      st["timetable svc"]
      sg -->|"hop"| sa
      sa -->|"hop"| st
    end
    note["report card: 0 hops ┬╖ 0 hops ┬╖ 2 hops (+2 ms, 99.8%)"]

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/software-architecture/lesson-diagrams.html#l06

тЭУ What

ЁЯдФ Why

Because this is the decision with the biggest blast radius of all (lesson 01), and it is made too early more often than any other. Microservices do not remove coupling тАФ they move it onto the network, where it is slower, can fail halfway, and is harder to see. The modular monolith gets most of the boundary benefits first, and keeps the door open.

ЁЯФз How (in this repo)

TRACES in arch/models.py lists which departments three requests pass through (a report card touches grades тЖТ admissions тЖТ timetable). styles(traces, departments, hop_ms, hop_fail) counts, per style, the network hops (a call between two departments that crosses a process boundary), the added latency (hops ├Ч hop_ms) and the success chance ((1 тИТ hop_fail) ^ hops). The defaults тАФ 1 ms and 1 failure in 1000 per hop тАФ are teaching assumptions, not measurements; the snippet changes them.

ЁЯзк Try it

python3 arch/demo.py styles
python3 - <<'EOF'
import sys; sys.path.insert(0, "arch"); from models import styles
for ms, fail in ((1, 0.001), (5, 0.01)):
    r = styles(hop_ms=ms, hop_fail=fail)[2]["per"]["enrol"]
    print(f"microservices, {ms} ms and {fail:.1%} failure per hop тЖТ enrol: {r[0]} hops, +{r[1]:.0f} ms, succeeds {r[2]:.2%}")
chatty = {"report card": ["web"] + ["grades", "admissions"] * 5}
r = styles(chatty, hop_ms=1, hop_fail=0.001)[2]["per"]["report card"]
print(f"a chatty report card with {r[0]} hops тЖТ +{r[1]:.0f} ms, succeeds {r[2]:.2%}")
EOF

тЬЕ Verify тАФ what you should see

styles prints:

тФАтФА the same 4 departments in three styles (teaching assumptions: 1 ms and 1-in-1000 failure per network hop)
   monolith         1 deployable unit(s) ┬╖ own deploys: no  ┬╖ boundaries enforced by nothing (any room may import any room)
   modular monolith 1 deployable unit(s) ┬╖ own deploys: no  ┬╖ boundaries enforced by a build-time check (lesson 09)
   microservices    4 deployable unit(s) ┬╖ own deploys: yes ┬╖ boundaries enforced by the network (separate processes)
                    report card 2 hop(s) +2 ms 99.8% ┬╖ pay fees 1 hop(s) +1 ms 99.9% ┬╖ enrol 3 hop(s) +3 ms 99.7% ┬╖ enrol needs a saga across services

Your snippet prints:

microservices, 1 ms and 0.1% failure per hop тЖТ enrol: 3 hops, +3 ms, succeeds 99.70%
microservices, 5 ms and 1.0% failure per hop тЖТ enrol: 3 hops, +15 ms, succeeds 97.03%
a chatty report card with 10 hops тЖТ +10 ms, succeeds 99.00%

ЁЯПБ What you just proved

The two monolith styles have 0 hops: every call between departments is a function call, and enrolment is one database transaction. Microservices give each department its own deploy тАФ the one thing the others cannot тАФ and pay for it on every request: enrol crosses 3 hops and becomes a saga. With worse (but realistic for a busy network) numbers, 5 ms and 1% per hop, about 3 enrolments in 100 fail somewhere in the middle (97.03% succeed) unless you add retries, timeouts and compensation. And a chatty design тАФ 10 small calls instead of one тАФ multiplies both costs. The modular monolith is the only style whose boundaries are checked without paying the network toll.

тЪая╕П Common mistakes

ЁЯПн In production

On a real project тАФ a modular monolith needs a boundary checker, or it decays into a plain monolith within a year:

# packs/grades/package.yml
enforce_dependencies: true
dependencies:
  - packs/admissions
bin/packwerk check      # fails on a reference to a package not listed in dependencies
@Test
void departmentsAreModules() {
    ApplicationModules.of(SchoolApplication.class).verify();
}

A widely discussed example of moving the other way: in 2023 the Amazon Prime Video team wrote that moving one audio/video monitoring service from distributed serverless components to a single process cut that service's infrastructure cost by over 90%. The lesson is not "never distribute" тАФ it is "distribute where the benefit pays the hop".

ЁЯПн Why this matters in production: before splitting a service out, write down which characteristic it buys (independent deploy? independent scaling? a different team?) and what it costs in hops per request. If you cannot name the first, keep the module.

тПня╕П Next

Even inside one building, departments can stop calling each other and start posting notices. Event-driven architecture and CQRS.

git checkout lesson-07-events-cqrs
тЖР PreviousdddNext тЖТevents cqrs

This page is the lesson's README from the lesson-06-styles branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.