Unit 08.03: Where the model calls live
One file should know which model provider you use.
The vendor boundary
Four files with what each knows.
The code lists them.
STRUCTURE = [
("app/routers/classify.py", "HTTP only", "validates, calls the service"),
("app/services/classify.py", "logic", "builds the prompt, calls the client"),
("app/clients/model.py", "boundary", "the only file that knows the provider"),
("app/models.py", "contract", "request and response shapes"),
]
print(f"{'file':28} {'role':10} contains")
for path, role, contains in STRUCTURE:
print(f"{path:28} {role:10} {contains}")
print("""
One file knows the provider. Swapping model vendors, adding a retry, or
injecting a fake for tests is a change in `clients/model.py` and nowhere else.
The service knows there is a model and not which one, which is what keeps the
logic testable without a network.
""")
Swapping vendors, adding a retry or injecting a fake is a change in the client file and nowhere else. The service knows there is a classifier and not which one.
That is also what makes the offline test suite possible. The service can be handed a fake that satisfies the same shape, and every rule in it is exercised without a network.
The mistake this prevents
The mistake is calling the provider's SDK directly from the service because it is one line. The vendor's types then spread through your codebase, and a change of provider becomes a change everywhere those types appear.
Takeaway
Confine the provider to one client module. The service depends on a shape, which is what makes both vendor changes and offline testing cheap.
