7 routes42 sections324 units

Unit 4.7 · about 3 h

25 / 324

Layer boundaries and command-line contracts

Providers change and transports multiply. When the domain depends on interfaces instead of concrete clients, a database or notifier swap stays local. When CLI and HTTP share one service layer, one fix covers both surfaces.

  • implement

You can now

  • Split a service into domain, repository and notifier owners with dependency inversion, and swap fakes without touching the service.
  • Expose one service layer through a CLI with typed arguments, stdout data, stderr errors and nonzero exit on failure.
  • Keep transport and persistence types out of the domain core across both surfaces.

Covers

  • Domain, service, transport and persistence boundaries
  • Command-line inputs, output streams, exit codes and errors

Study, in this order

4 steps

  1. Interfaces
    AlgoMaster Low-Level Design - one rehearsal

    1. What is an Interface?; 2. Key Properties of Interfaces; 4. Practical Example: Notification Service

  2. Dependency Inversion Principle
    AlgoMaster Low-Level Design - one rehearsal

    1. The Problem: A Tightly Coupled EmailService (Why Switching Providers Is Painful); 3. Applying DIP, steps 1 to 4; 4. Common Pitfalls While Applying DIP, especially No Actual Injection

  3. Bigger Applications: `APIRouter` layering
    FastAPI tutorial

    APIRouter; Dependencies; Include an APIRouter with a custom prefix, tags, responses, and dependencies. Routers stay thin and the service owns the rules.

  4. `argparse` and `subprocess` conventions
    Stas' Python Cookbook - data and interfaces

    The argparse built-in CLI parser block (positionals, flags, type, nargs, choices); Replaying the exact command line; then Subprocess and Shell Integration for returncode, stdout and stderr

    Depth · Skip the click, typer and fire pointers and the signal-handling block.

Focus on

  • The domain service takes repository and notifier interfaces in its constructor. Constructing concrete clients inside the service welds the boundary shut.
  • One sentence per class without and. A class that validates, prices, stores and notifies is four classes wearing one name.
  • argparse types and choices validate at the edge, and a bad argument already exits 2 with usage on stderr. Keep the same rule for domain failures: stdout carries data, stderr carries diagnostics, and failure exits nonzero with stdout left clean.
  • CLI and HTTP handlers call the same service function and map the same domain errors to the same outcomes.

Reproduce closed-book

Closed-book, split an order processor into service, inventory and notifier with injected fakes, and drive it from a CLI: show a successful order on stdout with exit 0, and a bad choice plus missing input on stderr with nonzero exit.

Traps

  • Business rules migrating into route handlers or CLI commands. Two surfaces then disagree; keep rules in the service and handlers thin.
  • Data mixed with logs on stdout. Pipes and files capture stdout, so diagnostics go to stderr or downstream parsing breaks.
  • Exit code always 0. A failed command that reports success poisons scripts and supervision; assert nonzero on every failure path.

Practice

AlgoMaster Refactor Order Processing

Full problem: keep the public API (placeOrder, stockOf, orderCount, confirmations) while moving stock into InventoryManager and confirmation history into NotificationService, both injectable through the OrderProcessor constructor; rejected orders leave stock, order IDs and confirmations untouched

Solve it from the Python starter, then reuse the split in your service: one test swaps both collaborators for fakes without editing OrderProcessor.

Self-test

  1. How do you swap the database for a fake without touching the service?

  2. Where do stdout and stderr go in a correct CLI, and what exits nonzero?

  3. A route handler starts checking stock levels directly. What broke?

Completion criterion

Stop when

The service file imports no concrete adapter, the CLI drives success and two failure modes with correct streams and exits, and the fakes swap without service edits.

Mark passed, then open 4.8 Only mark this when you can do it without notes.

Leads to

Comes after

Resources for this unit

  • FastAPI tutorial
    FastAPIsupportimplementation
    Use Request Body, Dependencies, Handling Errors and Testing to expose the same service layer through HTTP. Keep framework models outside the domain core and preserve the same error semantics as the CLI.
  • AlgoMaster Low-Level Design - one rehearsal
    AlgoMastersupportpractice
    Use one fitting design prompt to rehearse interface contracts, dependency direction and failure ownership. Treat the review as feedback, then verify behavior with your own tests.
  • Stas' Python Cookbook - data and interfaces
    Stas Bekmansupportreference
    Use Serialization and Deserialization plus Environment and Arguments for focused implementation help. Compare JSON, JSONL, CSV and compressed data, and never use pickle for untrusted interchange.

Also in this section

  • Official docs typed-service build path
    Python typing, Pydantic, FastAPI and PostgreSQLprimarycourse
    Build one record service in this order: structural protocols and generics; Pydantic models, validators, strict mode, serialization and settings; FastAPI request bodies, dependencies, errors and tests; then PostgreSQL transactions and EXPLAIN. Replay valid, malformed, duplicate and previous-schema requests through CLI and HTTP.
  • AlgoMaster SQL - selected route
    AlgoMasterrequiredcourse
    Study SQL Foundations, Querying Data, Joins, Subqueries and CTEs, Window Functions, DDL, DML and Views, Performance Optimization, Transactions and Concurrency, and Data Modeling. Stop when the service queries and transaction tests pass.
  • PostgreSQL transactions and EXPLAIN
    PostgreSQLrequiredreference
    Use the transaction tutorial and EXPLAIN guide to demonstrate commit, rollback, one isolation anomaly or prevented anomaly, and a measured plan improvement on the database actually used.
  • AlgoMaster Python - interfaces, generics and data files
    AlgoMastersupportcourse
    Use Interfaces and Protocols plus Generic Types for structural typing, and Working with JSON plus Working with CSV for JSONL, error handling, newline and DictReader behavior. Lesson quizzes check recall; implementation is verified with your own scripts.
  • SQLAlchemy 2.1 documentation - select, Session and eager loading
    SQLAlchemyreferencereference
    Use the select() tutorial for 2.x query style, Session basics for the transaction boundary and identity map, and relationship loading for lazy versus selectinload versus joinedload plus raiseload. Use select() style and Session.execute or Session.scalars only, never the legacy Query API.
  • Alembic tutorial - migration scripts and downgrading
    SQLAlchemysupportreference
    Use Create a Migration Script, Running our First and Second Migration, Getting Information and Downgrading to run a paired upgrade and downgrade round trip on a scratch database.
  • PyArrow Parquet read and write docs
    Apache Arrowsupportreference
    Use Reading and Writing Single Files plus Table from_pydict and from_pylist to convert normalized records to Parquet and test types, nulls, column projection and schema.
  • Stripe idempotent requests
    Stripesupportreference
    Use the Idempotency-Key header contract, stored-response replay, parameter-mismatch errors and POST-only scope to implement keyed replay for one endpoint.

Quick find

Search Michi

Type to search every route, section, unit, topic and resource.

Enter opens the first result Esc closes

Reference

Keyboard shortcuts

n
Continue to the first unit not passed
j / k
Next / previous unit
1 2 3
Toggle learned, practised, passed
/
Open search
?
Show this help
Esc
Close open panels