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

ЁЯПЫя╕П рдзрдбрд╛ 01 тАФ Architecture рдореНрд╣рдгрдЬреЗ рдХрд╛рдп: рдмрджрд▓рд╛рдпрд▓рд╛ рдорд╣рд╛рдЧ рдЕрд╕рд▓реЗрд▓реЗ рдирд┐рд░реНрдгрдп

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 01 ┬╖ рдкреБрдвреЗ: lesson-02-modularity


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

рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕ рдЬреНрдпрд╛ рдПрдХрд╛ рдХрд▓реНрдкрдиреЗрд╡рд░ рдЙрднрд╛ рдЖрд╣реЗ: architecture рдореНрд╣рдгрдЬреЗ рдирдВрддрд░ рдмрджрд▓рд╛рдпрд▓рд╛ рдорд╣рд╛рдЧ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдирд┐рд░реНрдгрдпрд╛рдВрдЪрд╛ рд╕рдВрдЪ тАФ рдЖрдгрд┐ рддреЗ рдирд┐рд░реНрдгрдп рдЬреЗ рдЧреБрдг (the "-ilities") рдорд┐рд│рд╡реВрди рджреЗрддрд╛рдд рдХрд┐рдВрд╡рд╛ рдЬреНрдпрд╛рдВрдЪреА рдХрд┐рдВрдордд рдШреЗрддрд╛рдд. рд╣рд╛ рдХреЛрд░реНрд╕ рдПрдХ рдЦрд░рд╛, рдЫреЛрдЯрд╛ codebase рдкрд╛рд╣рддреЛ: рдХрд╛рд╣реА terms рдордзреНрдпреЗ рд╡рд╛рдврд▓реЗрд▓реЗ рд╢рд╛рд│реЗрдЪреЗ рдПрдХ app. рдЖрдкрдг рддреЛ рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдЗрдорд╛рд░рддреАрдЪрд╛ рдирдХрд╛рд╢рд╛ рдореНрд╣рдгреВрди рд╡рд╛рдЪрддреЛ: modules рдореНрд╣рдгрдЬреЗ рдЦреЛрд▓реНрдпрд╛, imports рдореНрд╣рдгрдЬреЗ рдЦреЛрд▓реНрдпрд╛рдВрдордзреАрд▓ рдорд╛рд░реНрдЧрд┐рдХрд╛ рдЖрдгрд┐ рджрд╛рд░реЗ, рдЖрдгрд┐ layers рдореНрд╣рдгрдЬреЗ рдордЬрд▓реЗ. рдкреВрд░реНрдг рдХреЛрд░реНрд╕рднрд░ рддреБрдореНрд╣реА рд╡рд╛рдкрд░рд╛рд▓ рдЕрд╢рд╛ рдЦрд▒реНрдпрд╛ files:

ЁЯОТ рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рдЖрдзреА: рддреБрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд Python 3 рд▓рд╛рдЧреЗрд▓, рдмрд╛рдХреА рдХрд╛рд╣реАрд╣реА рдирд╛рд╣реА тАФ pip install рдирд╛рд╣реА, cloud account рдирд╛рд╣реА. Analyzer sample рдЪреНрдпрд╛ source files рдлрдХреНрдд рд╡рд╛рдЪрддреЛ; рддреЛ рддреНрдпрд╛ рдХрдзреАрдЪ рдЪрд╛рд▓рд╡рдд рдирд╛рд╣реА. рд╕рдЧрд│реЗ sorted рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдкреНрд░рддреНрдпреЗрдХ run рддреЗрдЪ рдЖрдХрдбреЗ рдЫрд╛рдкрддреЛ. рдЬрд┐рдереЗ lab рд╕реЛрдкреЗ рдХрд░рддреЛ (network hop рдЪреА рдХрд┐рдВрдордд, trade-off table рдордзреАрд▓ рдЧреБрдг), рддрд┐рдереЗ рдзрдбрд╛ teaching model рдЕрд╕реЗ рд╕рд╛рдВрдЧрддреЛ. on a real project рдЕрд╢реА рдЦреВрдг рдЕрд╕рд▓реЗрд▓реЗ commands рдЦрд░реА tools (import-linter, ArchUnit, Structurizr) рд╡рд╛рдкрд░рддрд╛рдд, рдЬреА рддреБрдореНрд╣реА рд╕реНрд╡рддрдГ install рдХрд░рддрд╛.

ЁЯзн рд╣реЗ рдХреБрдареЗ рдмрд╕рддреЗ: System Design рд╢рд╛рд│рд╛ distributed systems рдбрд┐рдЭрд╛рдЗрди рдХрд░рддреЗ (caches, queues, sharding, рдкреВрд░реНрдг рдХреЗрд▓реЗрд▓реА designs). рд╣реА рд╢рд╛рд│рд╛ codebase рдЪреНрдпрд╛ рдЖрддрд▓реНрдпрд╛ рдЖрдгрд┐ codebases рдЪреНрдпрд╛ рдордзрд▓реНрдпрд╛ рд░рдЪрдиреЗрдмрджреНрджрд▓ рдЖрд╣реЗ: рдЦреЛрд▓реНрдпрд╛, рдорд╛рд░реНрдЧрд┐рдХрд╛, рдордЬрд▓реЗ рдЖрдгрд┐ рд╡рд┐рднрд╛рдЧ.

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

рдХрддрд░рд┐рдирд╛ рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдирд╡реНрдпрд╛ рдЗрдорд╛рд░рддреАрдЪреЗ рдирд┐рдпреЛрдЬрди рдХрд░рддреЗ рдЖрд╣реЗ. ЁЯПл рдХрд╛рд╣реА рдирд┐рд░реНрдгрдп рдирдВрддрд░ рд╕рд╣рдЬ рдмрджрд▓рддрд╛ рдпреЗрддрд╛рдд: рд╡рд░реНрдЧрд╛рдЪреНрдпрд╛ рднрд┐рдВрддреАрдВрдЪрд╛ рд░рдВрдЧ, posters рдХреБрдареЗ рд▓рд╛рд╡рд╛рдпрдЪреЗ. рдПрдХ weekend рдЖрдгрд┐ рд░рдВрдЧрд╛рдЪрд╛ рдмреНрд░рд╢, рдЭрд╛рд▓реЗ.

рдЗрддрд░ рдирд┐рд░реНрдгрдп рдирдВрддрд░ рдмрджрд▓рдгреЗ рдЦреВрдк рдЕрд╡рдШрдб рдЕрд╕рддреЗ: рдЬрд┐рдирд╛ рдХреБрдареЗ рдЕрд╕реЗрд▓, рдХреЛрдгрддреНрдпрд╛ рднрд┐рдВрддреА рдЫрдд рддреЛрд▓реВрди рдзрд░рддрд╛рдд, рдХрд┐рддреА рдордЬрд▓реЗ рдЖрд╣реЗрдд. рдЗрдорд╛рд░рдд рдореБрд▓рд╛рдВрдиреА рднрд░рд▓реА рдХреА рдЬрд┐рдирд╛ рд╣рд▓рд╡рдгреЗ рдореНрд╣рдгрдЬреЗ рдЕрд░реНрдзреА рд╢рд╛рд│рд╛ рдорд╣рд┐рдиреЛрдирдорд╣рд┐рдиреЗ рдмрдВрдж рдареЗрд╡рдгреЗ.

Architecture рдореНрд╣рдгрдЬреЗ рд╣рд╛ рджреБрд╕рд▒реНрдпрд╛ рдкреНрд░рдХрд╛рд░рдЪрд╛ рдирд┐рд░реНрдгрдп. рддреЛ рдЬрд┐рдирд╛ рдЖрд╣реЗ, рд░рдВрдЧ рдирд╡реНрд╣реЗ.

рдЖрдгрд┐ рдкрд░рд┐рдкреВрд░реНрдг рдЗрдорд╛рд░рдд рдЕрд╢реА рдХреЛрдгрддреАрдЪ рдирд╕рддреЗ. рдЕрдиреЗрдХ рджрд╛рд░реЗ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЗрдорд╛рд░рддреАрдд рдлрд┐рд░рдгреЗ рд╕реЛрдкреЗ, рдкрдг рддреА рд╕реБрд░рдХреНрд╖рд┐рдд рдареЗрд╡рдгреЗ рдЕрд╡рдШрдб. рдЬрд╛рдб рднрд┐рдВрддреАрдВрдЪреА рдЗрдорд╛рд░рдд рд╢рд╛рдВрдд рдЕрд╕рддреЗ, рдкрдг рдорд╣рд╛рдЧ рдкрдбрддреЗ. рдХрддрд░рд┐рдирд╛рд▓рд╛ рдпрд╛ рд╢рд╛рд│реЗрд╕рд╛рдареА рд╕рдЧрд│реНрдпрд╛рдд рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдереЛрдбреЗрдЪ рдЧреБрдг рдирд┐рд╡рдбрд╛рд╡реЗ рд▓рд╛рдЧрддрд╛рдд тАФ рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪреА рдХрд┐рдВрдордд рд╕реНрд╡реАрдХрд╛рд░рд╛рд╡реА рд▓рд╛рдЧрддреЗ.

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

flowchart LR
    subgraph cheap["ЁЯОи cheap to change (design)"]
      v["rename a variable<br/>touches 1 room"]
      p["repaint a room<br/>(a private function)"]
    end
    subgraph costly["ЁЯПЧя╕П expensive to change (architecture)"]
      port["change domain.ports<br/>ripples into 8 of 12 rooms"]
      adm["change domain.admissions<br/>ripples into 8 of 12 rooms"]
    end
    ilities["тЪЦя╕П the -ilities Katrina picks:<br/>modifiability ┬╖ testability ┬╖ availability"]
    costly --> ilities

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рд╕реНрд╡рд╕реНрдд рдирд┐рд░реНрдгрдп рдЖрдкреЛрдЖрдк рд╕реБрдзрд╛рд░рддрд╛рдд рдЖрдгрд┐ рдорд╣рд╛рдЧ рдирд┐рд░реНрдгрдп рд╕реБрдзрд╛рд░рдд рдирд╛рд╣реАрдд. рд╡рд╛рдИрдЯ рдирд╛рд╡ рджрд┐рд▓реЗрд▓реНрдпрд╛ variable рдЪреА рдХрд┐рдВрдордд рдПрдХ рдорд┐рдирд┐рдЯ. рд╕рдЧрд│реЗ рдЬрдг import рдХрд░рддрд╛рдд рдЖрдгрд┐ рдЬреНрдпрд╛рдд database рдорд┐рд╕рд│рд▓реЗрд▓рд╛ рдЖрд╣реЗ рдЕрд╕реЗ core module, рдирд┐рдпрдо рдмрджрд▓рд▓реЗ рдХреА рджрд░ рд╡реЗрд│реА рдЖрдард╡рдбреЗ рдЦрд╛рддреЗ тАФ рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ рдЯреЗрдХрдгрд╛рд▒реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдирд╡реНрдпрд╛ рдЦреЛрд▓реАрд╕реЛрдмрдд рддреА рдХрд┐рдВрдордд рд╡рд╛рдврддреЗ. рдХреЛрдгрддреЗ рдирд┐рд░реНрдгрдп рдорд╣рд╛рдЧ рдЖрд╣реЗрдд рд╣реЗ рдорд╛рд╣реАрдд рдЕрд╕рд▓реЗ рдХреА рд╡рд┐рдЪрд╛рд░рд╛рдЪрд╛ рд╡реЗрд│ рдХреБрдареЗ рдШрд╛рд▓рд╡рд╛рдпрдЪрд╛ рддреЗ рдХрд│рддреЗ. Driving -ilities рдирд╛ рдирд╛рд╡ рджрд┐рд▓реЗ рдХреА рддреБрдореНрд╣реА рдХрд╢рд╛рд╕рд╛рдареА optimise рдХрд░рдд рдЖрд╣рд╛рдд рддреЗ рдХрд│рддреЗ, рдЖрдгрд┐ рджреЛрди рдЪрд╛рдВрдЧрд▓реЗ engineers рдЖрд╡рдбреАрдирд┐рд╡рдбреАрд╡рд░ рд╡рд╛рдж рдШрд╛рд▓рдгреЗ рдерд╛рдВрдмрд╡реВрди trade-offs рдЪреА рддреБрд▓рдирд╛ рдХрд░реВ рд▓рд╛рдЧрддрд╛рдд.

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

arch/analyze.py рдордзреАрд▓ scan() arch/sample/schoolapp/ рдордзреВрди рдлрд┐рд░рддреЛ, рдкреНрд░рддреНрдпреЗрдХ module parse рдХрд░рддреЛ (рд░рд┐рдХрд╛рдореНрдпрд╛ __init__.py files рд╕реЛрдбреВрди) рдЖрдгрд┐ рддреНрдпрд╛рдЪреЗ imports рдПрдХрд╛ graph рдореНрд╣рдгреВрди рдиреЛрдВрджрд╡рддреЛ: {room: [rooms it imports]}. dependents(g, m) рдорд╛рд░реНрдЧрд┐рдХрд╛рдВрд╡рд░реВрди рдЙрд▓рдЯ рдЪрд╛рд▓рдд m рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрдгрд╛рд░реА рдкреНрд░рддреНрдпреЗрдХ рдЦреЛрд▓реА рд╢реЛрдзрддреЛ; reach(g, m) рддреНрдпрд╛рдВрд╡рд░реВрди рдкреБрдвреЗ рдЪрд╛рд▓рддреЛ. arch/demo.py рдордзреАрд▓ decisions() рд╕рдЧрд│реНрдпрд╛рдд рдореЛрдареЗ рдЖрдгрд┐ рд╕рдЧрд│реНрдпрд╛рдд рдЫреЛрдЯреЗ blast radii рдЖрдгрд┐ arch/models.py рдордзреАрд▓ DRIVING рдордзреВрди рдХрддрд░рд┐рдирд╛рдЪреЗ рддреАрди driving characteristics рдЫрд╛рдкрддреЛ.

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

python3 arch/demo.py decisions
python3 - <<'EOF'
import sys; sys.path.insert(0, "arch"); from analyze import scan, dependents, reach
g = scan().imports
for m in sorted(g, key=lambda m: (-len(dependents(g, m)), m)):
    print(f"{m:<18} used by {len(dependents(g, m)):>2} rooms ┬╖ uses {len(reach(g, m)):>2} rooms")
EOF
python3 arch/test_arch.py

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

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

тФАтФА the building: arch/sample/schoolapp has 13 rooms (modules) and 24 corridors (imports), 187 lines
   blast radius = the rooms a change can ripple into (every room that reaches it through corridors):
   domain.admissions   8 of 12 rooms
   domain.ports        8 of 12 rooms
   domain.grades       4 of 12 rooms
   domain.timetable    0 of 12 rooms
   main                0 of 12 rooms

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

domain.admissions  used by  8 rooms ┬╖ uses  0 rooms
domain.ports       used by  8 rooms ┬╖ uses  0 rooms
domain.grades      used by  4 rooms ┬╖ uses  3 rooms
infra.db           used by  4 rooms ┬╖ uses  3 rooms
domain.fees        used by  3 rooms ┬╖ uses  1 rooms
infra.email        used by  3 rooms ┬╖ uses  1 rooms
web.format         used by  3 rooms ┬╖ uses  0 rooms
app.enrol          used by  2 rooms ┬╖ uses  3 rooms
app.fees_service   used by  2 rooms ┬╖ uses  3 rooms
app.report_card    used by  2 rooms ┬╖ uses  4 rooms
web.views          used by  1 rooms ┬╖ uses 10 rooms
domain.timetable   used by  0 rooms ┬╖ uses  1 rooms
main               used by  0 rooms ┬╖ uses 11 rooms

Tests рд╢реЗрд╡рдЯреА 12/12 passed рдЫрд╛рдкрддрд╛рдд.

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

рдПрдХрд╛рдЪ рдЫреЛрдЯреНрдпрд╛ codebase рдордзреНрдпреЗ рд╕реНрд╡рд╕реНрдд рдЦреЛрд▓реНрдпрд╛ рдЖрдгрд┐ рдорд╣рд╛рдЧ рдЦреЛрд▓реНрдпрд╛ рдЕрд╕рддрд╛рдд. domain.timetable рдмрджрд▓рд▓реА рддрд░ рдЗрддрд░ рдХреЛрдгрд╛рд▓рд╛рд╣реА рдзрдХреНрдХрд╛ рд▓рд╛рдЧрдд рдирд╛рд╣реА (0 dependents). domain.ports рдХрд┐рдВрд╡рд╛ domain.admissions рдЪрд╛ рдЖрдХрд╛рд░ рдмрджрд▓рд▓рд╛ рддрд░ рддреЛ рдмрджрд▓ рдЗрддрд░ 12 рдкреИрдХреА 8 рдЦреЛрд▓реНрдпрд╛рдВрдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪреВ рд╢рдХрддреЛ. рд╣рд╛ рдлрд░рдХ рдореНрд╣рдгрдЬреЗрдЪ architecture. рдЖрдгрд┐ web.format рдкрд╛рд╣рд╛: рдПрдХ рдЫреЛрдЯрд╛ presentation helper, рдЬреНрдпрд╛рдЪреЗ 3 dependents рдЖрд╣реЗрдд тАФ рддреНрдпрд╛рддрд▓реА рдПрдХ domain.timetable, рдПрдХ core рдЦреЛрд▓реА рдЬреА рд╡рд░рдЪреНрдпрд╛ рдордЬрд▓реНрдпрд╛рдкрд░реНрдпрдВрдд рд╡рд░ рдкреЛрд╣реЛрдЪрд▓реА. рдзрдбреЗ 03 рдЖрдгрд┐ 04 рд╣реА рдорд╛рд░реНрдЧрд┐рдХрд╛ рдкрдХрдбрддрд╛рдд.

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

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

On a real project тАФ git history рдордзреВрди рд╕рдЧрд│реНрдпрд╛рдд рдЬрд╛рд╕реНрдд рдмрджрд▓рдгрд╛рд▒реНрдпрд╛ рдЦреЛрд▓реНрдпрд╛ (hot spots) рд╢реЛрдзрд╛; рдореЛрдард╛ blast radius рдЕрд╕рд▓реЗрд▓рд╛ hot spot рдореНрд╣рдгрдЬреЗ рдЬрд┐рдереЗ architecture рдЪреЗ рдХрд╛рдо рд╕рдЧрд│реНрдпрд╛рдд рдЖрдзреА рдлрд│ рджреЗрддреЗ:

git log --since="1 year ago" --format= --name-only -- '*.py' | sort | uniq -c | sort -rn | head -10

рдПрдХ import рдкреНрд░рддреНрдпрдХреНрд╖рд╛рдд рдХрд╛рдп load рдХрд░рддреЛ, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ import рд▓рд╛ рдХрд┐рддреА рд╡реЗрд│ рд▓рд╛рдЧрддреЛ, рд╣реЗ Python рджрд╛рдЦрд╡реВ рд╢рдХрддреЛ:

python3 -X importtime -c "import yourapp.domain.grades" 2>&1 | tail -15

Driving characteristics code рдЪреНрдпрд╛ рд╢реЗрдЬрд╛рд░реА scenarios рдореНрд╣рдгреВрди рд▓рд┐рд╣реВрди рдареЗрд╡рд╛, рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде docs/architecture/characteristics.md рдордзреНрдпреЗ:

testability   тАФ the marks rules run in a unit test with no database, in under 1 s
modifiability тАФ a new department ships within one term without editing the others
availability  тАФ report cards open on results day (99.9% of requests succeed)

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рддреБрдордЪреНрдпрд╛ рдкреБрдврдЪреНрдпрд╛ design meeting рдЖрдзреА рддреАрди driving characteristics рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХреА рдПрдХ scenario рд▓рд┐рд╣рд╛. рдордЧ рд╕рдореЛрд░рдЪреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реНрдпрд╛рдпрд╛рд╕рд╛рдареА рд╡рд┐рдЪрд╛рд░рд╛: рддреЛ рдХреЛрдгрддреНрдпрд╛ characteristic рд▓рд╛ рдорджрдд рдХрд░рддреЛ рдЖрдгрд┐ рдХреЛрдгрддреНрдпрд╛рдЪреА рдХрд┐рдВрдордд рдШреЗрддреЛ.

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

Blast radius рдорд╛рд░реНрдЧрд┐рдХрд╛рдВрдиреА рдмрдирд▓реЗрд▓рд╛ рдЕрд╕рддреЛ. рдПрдХрд╛ рдЦреЛрд▓реАрд▓рд╛ рдХрд┐рддреА рдорд╛рд░реНрдЧрд┐рдХрд╛ рдЕрд╕рд╛рд╡реНрдпрд╛рдд тАФ рдЖрдгрд┐ рдХреЛрдгрддреНрдпрд╛? Coupling рдЖрдгрд┐ cohesion, рдореЛрдЬреВрди.

git checkout lesson-02-modularity

ЁЯПЫя╕П Lesson 01 тАФ What architecture is: the decisions that are expensive to change

ЁЯУН You are here: Lesson 01 of 12 ┬╖ Next: lesson-02-modularity


ЁЯУж What's in this branch

The one idea this whole course hangs on: architecture is the set of decisions that are expensive to change later тАФ and the qualities (the "-ilities") those decisions buy or cost. The course looks at a real, tiny codebase: a school app that grew for a few terms. We read it as the school building's floor plan: modules are rooms, imports are corridors and doors between rooms, and layers are floors. Real files you will use all the way through:

ЁЯОТ Before you start: you need Python 3 and nothing else тАФ no pip install, no cloud account. The analyzer only reads the sample's source files; it never runs them. Everything is sorted, so every run prints the same numbers. Where the lab simplifies (the cost of a network hop, the scores in a trade-off table), the lesson says teaching model. Commands marked on a real project use real tools (import-linter, ArchUnit, Structurizr) that you install yourself.

ЁЯзн Where this sits: the System Design school designs distributed systems (caches, queues, sharding, worked designs). This school is about the structure inside a codebase and between codebases: rooms, corridors, floors and departments.

ЁЯзТ Explain like I'm 5

Katrina is planning a new school building. ЁЯПл Some choices are easy to change later: the colour of the classroom walls, where the posters go. A weekend with a paint brush, done.

Other choices are very hard to change later: where the staircase goes, which walls hold up the roof, how many floors there are. Once the building is full of children, moving the staircase means closing half the school for months.

Architecture is the second kind of choice. It is the staircase, not the paint.

And there is no perfect building. A building with many doors is easy to walk through but hard to keep safe. A building with thick walls is quiet but costs more. Katrina has to pick the few qualities that matter most for this school тАФ and accept what they cost.

ЁЯЧ║я╕П Diagram

flowchart LR
    subgraph cheap["ЁЯОи cheap to change (design)"]
      v["rename a variable<br/>touches 1 room"]
      p["repaint a room<br/>(a private function)"]
    end
    subgraph costly["ЁЯПЧя╕П expensive to change (architecture)"]
      port["change domain.ports<br/>ripples into 8 of 12 rooms"]
      adm["change domain.admissions<br/>ripples into 8 of 12 rooms"]
    end
    ilities["тЪЦя╕П the -ilities Katrina picks:<br/>modifiability ┬╖ testability ┬╖ availability"]
    costly --> ilities

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

тЭУ What

ЁЯдФ Why

Because the cheap decisions fix themselves and the expensive ones do not. A badly named variable costs a minute. A core module that everything imports, with the database mixed in, costs weeks every time the rules change тАФ and the cost grows with every new room that leans on it. Knowing which decisions are expensive tells you where to spend your thinking time. Naming the driving -ilities tells you what you are optimising for, so two good engineers stop arguing about taste and start comparing trade-offs.

ЁЯФз How (in this repo)

scan() in arch/analyze.py walks arch/sample/schoolapp/, parses each module (not the empty __init__.py files) and records its imports as a graph: {room: [rooms it imports]}. dependents(g, m) walks the corridors backwards to find every room that reaches m; reach(g, m) walks them forwards. decisions() in arch/demo.py prints the biggest and smallest blast radii and Katrina's three driving characteristics from DRIVING in arch/models.py.

ЁЯзк Try it

python3 arch/demo.py decisions
python3 - <<'EOF'
import sys; sys.path.insert(0, "arch"); from analyze import scan, dependents, reach
g = scan().imports
for m in sorted(g, key=lambda m: (-len(dependents(g, m)), m)):
    print(f"{m:<18} used by {len(dependents(g, m)):>2} rooms ┬╖ uses {len(reach(g, m)):>2} rooms")
EOF
python3 arch/test_arch.py

тЬЕ Verify тАФ what you should see

decisions prints:

тФАтФА the building: arch/sample/schoolapp has 13 rooms (modules) and 24 corridors (imports), 187 lines
   blast radius = the rooms a change can ripple into (every room that reaches it through corridors):
   domain.admissions   8 of 12 rooms
   domain.ports        8 of 12 rooms
   domain.grades       4 of 12 rooms
   domain.timetable    0 of 12 rooms
   main                0 of 12 rooms

Your snippet prints:

domain.admissions  used by  8 rooms ┬╖ uses  0 rooms
domain.ports       used by  8 rooms ┬╖ uses  0 rooms
domain.grades      used by  4 rooms ┬╖ uses  3 rooms
infra.db           used by  4 rooms ┬╖ uses  3 rooms
domain.fees        used by  3 rooms ┬╖ uses  1 rooms
infra.email        used by  3 rooms ┬╖ uses  1 rooms
web.format         used by  3 rooms ┬╖ uses  0 rooms
app.enrol          used by  2 rooms ┬╖ uses  3 rooms
app.fees_service   used by  2 rooms ┬╖ uses  3 rooms
app.report_card    used by  2 rooms ┬╖ uses  4 rooms
web.views          used by  1 rooms ┬╖ uses 10 rooms
domain.timetable   used by  0 rooms ┬╖ uses  1 rooms
main               used by  0 rooms ┬╖ uses 11 rooms

The tests end with 12/12 passed.

ЁЯПБ What you just proved

The same small codebase has cheap rooms and expensive rooms. Changing domain.timetable touches nobody else (0 dependents). Changing the shape of domain.ports or domain.admissions can ripple into 8 of the other 12 rooms. That difference is the architecture. And look at web.format: a tiny presentation helper with 3 dependents тАФ one of them is domain.timetable, a core room that reached up to the top floor. Lessons 03 and 04 catch that corridor.

тЪая╕П Common mistakes

ЁЯПн In production

On a real project тАФ find the rooms that change most often (the hot spots) from git history; a hot spot with a big blast radius is where architecture work pays back first:

git log --since="1 year ago" --format= --name-only -- '*.py' | sort | uniq -c | sort -rn | head -10

Python can show what one import really loads, and how long each import takes:

python3 -X importtime -c "import yourapp.domain.grades" 2>&1 | tail -15

Write the driving characteristics down as scenarios next to the code, for example in docs/architecture/characteristics.md:

testability   тАФ the marks rules run in a unit test with no database, in under 1 s
modifiability тАФ a new department ships within one term without editing the others
availability  тАФ report cards open on results day (99.9% of requests succeed)

ЁЯПн Why this matters in production: before your next design meeting, write the three driving characteristics and one scenario each. Then, for every option on the table, ask which characteristic it helps and which it costs.

тПня╕П Next

A blast radius is made of corridors. How many corridors should a room have тАФ and which ones? Coupling and cohesion, measured.

git checkout lesson-02-modularity
тЖР Course homeall lessonsNext тЖТmodularity

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