Layers
Splitting a large program into layers makes every part do one job:
- Domain: entities and business rules (
Task.complete()); depends on nothing (no DB, no web). - Services (use cases): scenarios such as "create a task", "complete it"; they coordinate the domain and repository interfaces.
- Infrastructure: DB, files, HTTP, email: all the "outside" details.
- UI / API: CLI, web pages, REST: talking to the user.
Related: SRP, coupling and cohesion.
The domain layer
Domain classes are plain Python objects: the rules live inside them and they know nothing about the database. That is why they can be tested in seconds.
from dataclasses import dataclass
@dataclass
class Task:
id: int
title: str
done: bool = False
def complete(self):
if self.done:
raise ValueError("task already done")
self.done = True
t = Task(1, "write tests")
t.complete()
print(t)
try:
t.complete()
except ValueError as e:
print("error:", e)Task(id=1, title='write tests', done=True)
error: task already done