Unit 4.7 · about 3 h
25 / 324Layer 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,
stdoutdata,stderrerrors 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
- InterfacesAlgoMaster Low-Level Design - one rehearsal
1. What is an Interface?; 2. Key Properties of Interfaces; 4. Practical Example: Notification Service
- Dependency Inversion PrincipleAlgoMaster 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
- Bigger Applications: `APIRouter` layeringFastAPI tutorial
APIRouter; Dependencies; Include anAPIRouterwith a custom prefix, tags, responses, and dependencies. Routers stay thin and the service owns the rules. - `argparse` and `subprocess` conventionsStas' Python Cookbook - data and interfaces
The
argparsebuilt-in CLI parser block (positionals, flags,type,nargs,choices); Replaying the exact command line; then Subprocess and Shell Integration forreturncode,stdoutandstderrDepth · 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.
argparsetypes andchoicesvalidate at the edge, and a bad argument already exits2with usage onstderr. Keep the same rule for domain failures:stdoutcarries data,stderrcarries diagnostics, and failure exits nonzero withstdoutleft clean.- CLI and HTTP handlers call the same service function and map the same domain errors to the same outcomes.
Reproduce closed-book
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 capturestdout, so diagnostics go tostderror 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
-
How do you swap the database for a fake without touching the service?
The service depends on a repository interface and receives it by construction. The test constructs the service with the fake. -
Where do
stdoutandstderrgo in a correct CLI, and what exits nonzero?Data onstdout, diagnostics onstderr. Every failure path exits nonzero withstdoutclean. -
A route handler starts checking stock levels directly. What broke?
The layer boundary: stock rules belong to the service and repository, and the handler should call the service and map its errors.
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
- 4.8 HTTP APIs, pagination, streaming and idempotency Expose the same service over HTTP with identical outcomes.
Comes after
- 3.8 Imports, virtual environments and src-layout packagingLayer boundaries and CLI contracts build on this package.
- 4.1 Typed interfaces, runtime validation and configurationExpose the same typed service through the CLI surface.
- 4.3 Schema versions, compatibility and migrationsKeep version checks at the service edge, not in handlers.
Resources for this unit
- FastAPI tutorial 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 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 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 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 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 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 Use Interfaces and Protocols plus Generic Types for structural typing, and Working with JSON plus Working with CSV for JSONL, error handling,
newlineandDictReaderbehavior. Lesson quizzes check recall; implementation is verified with your own scripts. - SQLAlchemy 2.1 documentation - select, Session and eager loading Use the
select()tutorial for 2.x query style,Sessionbasics for the transaction boundary and identity map, and relationship loading for lazy versusselectinloadversusjoinedloadplusraiseload. Useselect()style andSession.executeorSession.scalarsonly, never the legacyQueryAPI. - Alembic tutorial - migration scripts and downgrading Use Create a Migration Script, Running our First and Second Migration, Getting Information and Downgrading to run a paired
upgradeanddowngraderound trip on a scratch database. - PyArrow Parquet read and write docs Use Reading and Writing Single Files plus
Tablefrom_pydictandfrom_pylistto convert normalized records to Parquet and test types, nulls, column projection and schema. - Stripe idempotent requests Use the
Idempotency-Keyheader contract, stored-response replay, parameter-mismatch errors andPOST-only scope to implement keyed replay for one endpoint.