Entities and use cases skill

Entities and Use Cases form the two innermost circles of Clean Architecture.

by wondelai·MIT license·★ 2,235 Stars on the repo·GitHub ↗

Use now

Files of Entities and use cases

wondelai/main1 file
entities-use-cases.md
Show the full text348 lines

Entities and Use Cases

Entities and Use Cases form the two innermost circles of Clean Architecture. Entities contain Enterprise Business Rules -- the most general and highest-level rules. Use Cases contain Application Business Rules -- the automation rules specific to a particular application. Together, they represent the core value of the system, the code that is most worth protecting from external change.

This reference covers entity design, use case structure, the interactor pattern, input/output boundaries, request/response models, and strategies for keeping use cases focused.

Table of Contents

  1. Enterprise Business Rules (Entities)
  2. Application Business Rules (Use Cases)
  3. Request and Response Models
  4. Keeping Use Cases Focused

Enterprise Business Rules (Entities)

What Is an Entity?

An entity is an object within the system that embodies a small set of critical business rules operating on critical business data. The entity object either contains the critical business data or has easy access to it. The interface of the entity consists of the functions that implement the critical business rules.

Critical distinction: An entity is not a database row. It is not an ORM model. It is not a struct that merely holds data. An entity encapsulates business rules -- logic that would exist even if there were no computer system at all.

Characteristics of Well-Designed Entities

1. Framework-independent: Entities do not inherit from database base classes, do not carry ORM annotations, and do not import framework packages.

# WRONG: Entity coupled to ORM
class Order(db.Model):  # Inherits from SQLAlchemy
    __tablename__ = 'orders'
    id = db.Column(db.Integer, primary_key=True)
    total = db.Column(db.Float)

# RIGHT: Pure domain entity
class Order:
    def __init__(self, order_id: str, items: list[OrderItem], customer_id: str):
        self._id = order_id
        self._items = items
        self._customer_id = customer_id
        self._status = OrderStatus.PENDING

    def calculate_total(self) -> Money:
        subtotal = sum(item.price * item.quantity for item in self._items)
        return subtotal + self._calculate_tax(subtotal)

    def _calculate_tax(self, subtotal: Money) -> Money:
        # Business rule: tax calculation
        return subtotal * Decimal("0.08")

2. Business-rule containers: The methods on an entity enforce business invariants. They are not getters and setters -- they represent meaningful business operations.

class BankAccount:
    def withdraw(self, amount: Money) -> None:
        if amount > self._balance:
            raise InsufficientFundsError(self._balance, amount)
        if self._is_frozen:
            raise AccountFrozenError(self._account_id)
        self._balance -= amount
        self._record_transaction(TransactionType.WITHDRAWAL, amount)

The withdraw method encapsulates business rules: you cannot withdraw more than the balance, and you cannot withdraw from a frozen account. These rules exist regardless of whether the system is a web app, a mobile app, or a batch process.

3. Stable over time: Entities change only when business rules change. A decision to migrate from PostgreSQL to DynamoDB should not require any entity modifications. A decision to change the web framework should not affect entities.

4. Testable in complete isolation: You should be able to instantiate an entity and call its methods in a unit test with zero setup -- no database, no framework, no configuration files.

def test_order_calculates_total_with_tax():
    items = [OrderItem("widget", Money("10.00"), quantity=3)]
    order = Order("order-1", items, "customer-1")
    assert order.calculate_total() == Money("32.40")  # 30.00 + 2.40 tax
Entity Design Patterns
Pattern When to Use Example
Rich domain model Complex business rules with many invariants Order with status transitions, validation, calculations
Value objects Immutable concepts defined by their attributes Money(amount, currency), Address(street, city, zip)
Aggregates Cluster of entities treated as a unit for data changes Order aggregate contains OrderItems; external code accesses items only through Order
Domain events Communicate that something meaningful happened Order.place() produces OrderPlaced event
Factory methods Complex construction that enforces invariants Order.create(items, customer) validates and initializes
Common Entity Mistakes
Mistake Why It's Wrong Fix
Anemic entities (data-only, no behavior) Business rules scatter into services; entity is just a DTO Move business logic into entity methods
ORM annotations on domain entities Entity depends on database framework Separate domain entity from persistence model
Entity knows about its repository Entity depends on infrastructure Pass dependencies into use cases, not entities
Public setters on everything No invariant protection; any code can put entity in invalid state Use methods that enforce business rules; make fields private

Application Business Rules (Use Cases)

What Is a Use Case?

A Use Case describes a single, specific application operation. It orchestrates entities and defines the application-specific rules for how data flows to and from those entities. It accepts input through a defined port, manipulates entities, and produces output through another defined port.

Critical distinction: Use Cases are not entities. An entity encapsulates a business rule that would exist without software. A Use Case automates a specific application workflow that only makes sense within the context of the software system.

The Interactor Pattern

The Interactor is the concrete class that implements a Use Case. The pattern has three parts:

  1. Input Port (Input Boundary): An interface that defines what the Use Case accepts. The Controller calls this interface.
  2. Interactor: The concrete class that implements the Input Port and contains the application logic.
  3. Output Port (Output Boundary): An interface that defines what the Use Case produces. The Presenter implements this interface.
# Input Port -- defined in the Use Case circle
class PlaceOrderInput(ABC):
    @abstractmethod
    def execute(self, request: PlaceOrderRequest) -> None:
        pass

# Output Port -- defined in the Use Case circle
class PlaceOrderOutput(ABC):
    @abstractmethod
    def present_success(self, response: OrderResponse) -> None:
        pass

    @abstractmethod
    def present_validation_error(self, errors: list[str]) -> None:
        pass

    @abstractmethod
    def present_failure(self, message: str) -> None:
        pass

# Interactor -- implements Input Port, uses Output Port
class PlaceOrderInteractor(PlaceOrderInput):
    def __init__(self, order_repo: OrderRepository, presenter: PlaceOrderOutput):
        self._order_repo = order_repo
        self._presenter = presenter

    def execute(self, request: PlaceOrderRequest) -> None:
        errors = self._validate(request)
        if errors:
            self._presenter.present_validation_error(errors)
            return

        order = Order.create(
            items=[OrderItem(i.product_id, i.quantity, i.price) for i in request.items],
            customer_id=request.customer_id,
        )

        self._order_repo.save(order)

        response = OrderResponse(
            order_id=order.id,
            total=order.calculate_total(),
            status=order.status.value,
        )
        self._presenter.present_success(response)
Input/Output Boundaries

The boundaries are the interfaces that separate the Use Case circle from the circles on either side. They are defined in the Use Case circle and implemented by the outer circles.

Why boundaries matter:

  • The Controller depends on the Input Port (inward dependency -- correct)
  • The Presenter depends on the Output Port (inward dependency -- correct)
  • The Interactor depends on neither the Controller nor the Presenter (isolation preserved)
Alternative: Return-Based Use Cases

Not every use case needs the full Output Port pattern. A simpler approach returns a result directly:

class PlaceOrderInteractor:
    def __init__(self, order_repo: OrderRepository):
        self._order_repo = order_repo

    def execute(self, request: PlaceOrderRequest) -> Result[OrderResponse, OrderError]:
        errors = self._validate(request)
        if errors:
            return Failure(ValidationError(errors))

        order = Order.create(...)
        self._order_repo.save(order)
        return Success(OrderResponse(order.id, order.calculate_total()))

This is simpler and often sufficient. Use the full Output Port pattern when the presentation logic is complex or when you need to support multiple presentation formats from the same use case.

Request and Response Models

Request Models

Request models are simple data structures that carry input data across the boundary. They are defined in the Use Case circle.

Rules for request models:

  • No framework types (no HttpRequest, no Form, no JsonNode)
  • No entity types (the controller maps external data to the request model; the interactor maps the request model to entity calls)
  • Contain only primitives, strings, and simple nested structures
  • May contain validation hints but not validation logic dependent on external state
@dataclass(frozen=True)
class PlaceOrderRequest:
    customer_id: str
    items: list[OrderItemRequest]
    shipping_address: AddressRequest
    coupon_code: str | None = None

@dataclass(frozen=True)
class OrderItemRequest:
    product_id: str
    quantity: int
    unit_price: str  # String to avoid floating-point; Use Case converts to Money
Response Models

Response models carry output data across the boundary. They are defined in the Use Case circle.

Rules for response models:

  • No entity types -- the Use Case extracts the relevant data from entities and populates the response
  • No framework types -- the Presenter (outer circle) converts the response into whatever format the delivery mechanism needs
  • Contain only the data the outer circle needs to fulfill its role
@dataclass(frozen=True)
class OrderResponse:
    order_id: str
    total: str  # Formatted money value
    status: str
    estimated_delivery: str | None
    items: list[OrderItemResponse]

@dataclass(frozen=True)
class OrderItemResponse:
    product_name: str
    quantity: int
    line_total: str
The Mapping Chain

Data transforms at each boundary:

HTTP Request (JSON)
    --> Controller maps to --> PlaceOrderRequest (DTO)
        --> Interactor maps to --> Entity method calls
            --> Entity produces result
        --> Interactor maps to --> OrderResponse (DTO)
    --> Presenter maps to --> ViewModel or JSON
--> HTTP Response

Each transformation is a boundary crossing. Each boundary is an opportunity to decouple.

Keeping Use Cases Focused

One Use Case, One Operation

Each Use Case should represent a single application operation. If you find a Use Case doing multiple things, split it.

Signs of an unfocused Use Case:

  • The class name contains "And" (e.g., CreateAndNotifyOrder)
  • The execute method has conditional branches for fundamentally different operations
  • The class has more than 3-4 dependencies
  • The test file has tests for unrelated scenarios
Use Case Granularity Guidelines
Granularity Use Case Example Notes
Too coarse ManageOrders Does everything -- create, update, cancel, refund
Right level PlaceOrder, CancelOrder, RefundOrder Each is a single operation with clear input and output
Too fine ValidateOrderItems, CalculateOrderTotal These are steps within a use case, not standalone operations
Composing Use Cases

Sometimes one application operation involves multiple steps that could be their own use cases. Two approaches:

1. Use Case calls Use Case (simple composition):

class PlaceOrderAndSendConfirmation:
    def __init__(self, place_order: PlaceOrderInput, send_confirmation: SendConfirmationInput):
        self._place_order = place_order
        self._send_confirmation = send_confirmation

    def execute(self, request: PlaceOrderRequest) -> None:
        order_result = self._place_order.execute(request)
        if order_result.is_success:
            self._send_confirmation.execute(
                SendConfirmationRequest(order_result.order_id)
            )

2. Domain events (loose coupling):

The Use Case emits a domain event; another Use Case subscribes to it. This is better when the steps are truly independent and could happen asynchronously.

Use Case Dependencies

A Use Case should depend on:

  • Entity types (to call business rules)
  • Repository interfaces (to load and persist entities)
  • Output port interfaces (to present results)
  • Domain service interfaces (for cross-entity business operations)

A Use Case should NOT depend on:

  • Framework types (HTTP, ORM, message queue)
  • Concrete infrastructure classes (database client, email service)
  • Other use case concrete classes (use input port interfaces instead)
  • Configuration or environment variables (inject configuration as constructor parameters)
Testing Use Cases

Use Cases should be the most thoroughly tested part of the system because they contain the application's automation rules.

def test_place_order_calculates_total_and_saves():
    # Arrange
    mock_repo = MockOrderRepository()
    mock_presenter = MockPlaceOrderPresenter()
    interactor = PlaceOrderInteractor(mock_repo, mock_presenter)

    request = PlaceOrderRequest(
        customer_id="cust-1",
        items=[OrderItemRequest("prod-1", quantity=2, unit_price="25.00")],
        shipping_address=AddressRequest("123 Main", "Springfield", "62704"),
    )

    # Act
    interactor.execute(request)

    # Assert
    assert mock_repo.saved_order is not None
    assert mock_repo.saved_order.calculate_total() == Money("54.00")  # 50 + 4 tax
    assert mock_presenter.success_response.order_id == mock_repo.saved_order.id

No database. No web server. No framework. Just the use case logic running in a plain unit test. This is the payoff of the Dependency Rule.

1# Entities and Use Cases
2 
3Entities and Use Cases form the two innermost circles of Clean Architecture. Entities contain Enterprise Business Rules -- the most general and highest-level rules. Use Cases contain Application Business Rules -- the automation rules specific to a particular application. Together, they represent the core value of the system, the code that is most worth protecting from external change.
4 
5This reference covers entity design, use case structure, the interactor pattern, input/output boundaries, request/response models, and strategies for keeping use cases focused.
6 
7 
8## Table of Contents
91. [Enterprise Business Rules (Entities)](#enterprise-business-rules-entities)
102. [Application Business Rules (Use Cases)](#application-business-rules-use-cases)
113. [Request and Response Models](#request-and-response-models)
124. [Keeping Use Cases Focused](#keeping-use-cases-focused)
13 
14---
15 
16## Enterprise Business Rules (Entities)
17 
18### What Is an Entity?
19 
20An entity is an object within the system that embodies a small set of critical business rules operating on critical business data. The entity object either contains the critical business data or has easy access to it. The interface of the entity consists of the functions that implement the critical business rules.
21 
22**Critical distinction:** An entity is not a database row. It is not an ORM model. It is not a struct that merely holds data. An entity encapsulates business rules -- logic that would exist even if there were no computer system at all.
23 
24### Characteristics of Well-Designed Entities
25 
26**1. Framework-independent:**
27Entities do not inherit from database base classes, do not carry ORM annotations, and do not import framework packages.
28 
29```python
30# WRONG: Entity coupled to ORM
31class Order(db.Model): # Inherits from SQLAlchemy
32 __tablename__ = 'orders'
33 id = db.Column(db.Integer, primary_key=True)
34 total = db.Column(db.Float)
35 
36# RIGHT: Pure domain entity
37class Order:
38 def __init__(self, order_id: str, items: list[OrderItem], customer_id: str):
39 self._id = order_id
40 self._items = items
41 self._customer_id = customer_id
42 self._status = OrderStatus.PENDING
43 
44 def calculate_total(self) -> Money:
45 subtotal = sum(item.price * item.quantity for item in self._items)
46 return subtotal + self._calculate_tax(subtotal)
47 
48 def _calculate_tax(self, subtotal: Money) -> Money:
49 # Business rule: tax calculation
50 return subtotal * Decimal("0.08")
51```
52 
53**2. Business-rule containers:**
54The methods on an entity enforce business invariants. They are not getters and setters -- they represent meaningful business operations.
55 
56```python
57class BankAccount:
58 def withdraw(self, amount: Money) -> None:
59 if amount > self._balance:
60 raise InsufficientFundsError(self._balance, amount)
61 if self._is_frozen:
62 raise AccountFrozenError(self._account_id)
63 self._balance -= amount
64 self._record_transaction(TransactionType.WITHDRAWAL, amount)
65```
66 
67The `withdraw` method encapsulates business rules: you cannot withdraw more than the balance, and you cannot withdraw from a frozen account. These rules exist regardless of whether the system is a web app, a mobile app, or a batch process.
68 
69**3. Stable over time:**
70Entities change only when business rules change. A decision to migrate from PostgreSQL to DynamoDB should not require any entity modifications. A decision to change the web framework should not affect entities.
71 
72**4. Testable in complete isolation:**
73You should be able to instantiate an entity and call its methods in a unit test with zero setup -- no database, no framework, no configuration files.
74 
75```python
76def test_order_calculates_total_with_tax():
77 items = [OrderItem("widget", Money("10.00"), quantity=3)]
78 order = Order("order-1", items, "customer-1")
79 assert order.calculate_total() == Money("32.40") # 30.00 + 2.40 tax
80```
81 
82### Entity Design Patterns
83 
84| Pattern | When to Use | Example |
85|---------|-------------|---------|
86| **Rich domain model** | Complex business rules with many invariants | `Order` with status transitions, validation, calculations |
87| **Value objects** | Immutable concepts defined by their attributes | `Money(amount, currency)`, `Address(street, city, zip)` |
88| **Aggregates** | Cluster of entities treated as a unit for data changes | `Order` aggregate contains `OrderItems`; external code accesses items only through `Order` |
89| **Domain events** | Communicate that something meaningful happened | `Order.place()` produces `OrderPlaced` event |
90| **Factory methods** | Complex construction that enforces invariants | `Order.create(items, customer)` validates and initializes |
91 
92### Common Entity Mistakes
93 
94| Mistake | Why It's Wrong | Fix |
95|---------|---------------|-----|
96| Anemic entities (data-only, no behavior) | Business rules scatter into services; entity is just a DTO | Move business logic into entity methods |
97| ORM annotations on domain entities | Entity depends on database framework | Separate domain entity from persistence model |
98| Entity knows about its repository | Entity depends on infrastructure | Pass dependencies into use cases, not entities |
99| Public setters on everything | No invariant protection; any code can put entity in invalid state | Use methods that enforce business rules; make fields private |
100 
101## Application Business Rules (Use Cases)
102 
103### What Is a Use Case?
104 
105A Use Case describes a single, specific application operation. It orchestrates entities and defines the application-specific rules for how data flows to and from those entities. It accepts input through a defined port, manipulates entities, and produces output through another defined port.
106 
107**Critical distinction:** Use Cases are not entities. An entity encapsulates a business rule that would exist without software. A Use Case automates a specific application workflow that only makes sense within the context of the software system.
108 
109### The Interactor Pattern
110 
111The Interactor is the concrete class that implements a Use Case. The pattern has three parts:
112 
1131. **Input Port (Input Boundary):** An interface that defines what the Use Case accepts. The Controller calls this interface.
1142. **Interactor:** The concrete class that implements the Input Port and contains the application logic.
1153. **Output Port (Output Boundary):** An interface that defines what the Use Case produces. The Presenter implements this interface.
116 
117```python
118# Input Port -- defined in the Use Case circle
119class PlaceOrderInput(ABC):
120 @abstractmethod
121 def execute(self, request: PlaceOrderRequest) -> None:
122 pass
123 
124# Output Port -- defined in the Use Case circle
125class PlaceOrderOutput(ABC):
126 @abstractmethod
127 def present_success(self, response: OrderResponse) -> None:
128 pass
129 
130 @abstractmethod
131 def present_validation_error(self, errors: list[str]) -> None:
132 pass
133 
134 @abstractmethod
135 def present_failure(self, message: str) -> None:
136 pass
137 
138# Interactor -- implements Input Port, uses Output Port
139class PlaceOrderInteractor(PlaceOrderInput):
140 def __init__(self, order_repo: OrderRepository, presenter: PlaceOrderOutput):
141 self._order_repo = order_repo
142 self._presenter = presenter
143 
144 def execute(self, request: PlaceOrderRequest) -> None:
145 errors = self._validate(request)
146 if errors:
147 self._presenter.present_validation_error(errors)
148 return
149 
150 order = Order.create(
151 items=[OrderItem(i.product_id, i.quantity, i.price) for i in request.items],
152 customer_id=request.customer_id,
153 )
154 
155 self._order_repo.save(order)
156 
157 response = OrderResponse(
158 order_id=order.id,
159 total=order.calculate_total(),
160 status=order.status.value,
161 )
162 self._presenter.present_success(response)
163```
164 
165### Input/Output Boundaries
166 
167The boundaries are the interfaces that separate the Use Case circle from the circles on either side. They are defined in the Use Case circle and implemented by the outer circles.
168 
169**Why boundaries matter:**
170- The Controller depends on the Input Port (inward dependency -- correct)
171- The Presenter depends on the Output Port (inward dependency -- correct)
172- The Interactor depends on neither the Controller nor the Presenter (isolation preserved)
173 
174### Alternative: Return-Based Use Cases
175 
176Not every use case needs the full Output Port pattern. A simpler approach returns a result directly:
177 
178```python
179class PlaceOrderInteractor:
180 def __init__(self, order_repo: OrderRepository):
181 self._order_repo = order_repo
182 
183 def execute(self, request: PlaceOrderRequest) -> Result[OrderResponse, OrderError]:
184 errors = self._validate(request)
185 if errors:
186 return Failure(ValidationError(errors))
187 
188 order = Order.create(...)
189 self._order_repo.save(order)
190 return Success(OrderResponse(order.id, order.calculate_total()))
191```
192 
193This is simpler and often sufficient. Use the full Output Port pattern when the presentation logic is complex or when you need to support multiple presentation formats from the same use case.
194 
195## Request and Response Models
196 
197### Request Models
198 
199Request models are simple data structures that carry input data across the boundary. They are defined in the Use Case circle.
200 
201**Rules for request models:**
202- No framework types (no `HttpRequest`, no `Form`, no `JsonNode`)
203- No entity types (the controller maps external data to the request model; the interactor maps the request model to entity calls)
204- Contain only primitives, strings, and simple nested structures
205- May contain validation hints but not validation logic dependent on external state
206 
207```python
208@dataclass(frozen=True)
209class PlaceOrderRequest:
210 customer_id: str
211 items: list[OrderItemRequest]
212 shipping_address: AddressRequest
213 coupon_code: str | None = None
214 
215@dataclass(frozen=True)
216class OrderItemRequest:
217 product_id: str
218 quantity: int
219 unit_price: str # String to avoid floating-point; Use Case converts to Money
220```
221 
222### Response Models
223 
224Response models carry output data across the boundary. They are defined in the Use Case circle.
225 
226**Rules for response models:**
227- No entity types -- the Use Case extracts the relevant data from entities and populates the response
228- No framework types -- the Presenter (outer circle) converts the response into whatever format the delivery mechanism needs
229- Contain only the data the outer circle needs to fulfill its role
230 
231```python
232@dataclass(frozen=True)
233class OrderResponse:
234 order_id: str
235 total: str # Formatted money value
236 status: str
237 estimated_delivery: str | None
238 items: list[OrderItemResponse]
239 
240@dataclass(frozen=True)
241class OrderItemResponse:
242 product_name: str
243 quantity: int
244 line_total: str
245```
246 
247### The Mapping Chain
248 
249Data transforms at each boundary:
250 
251```
252HTTP Request (JSON)
253 --> Controller maps to --> PlaceOrderRequest (DTO)
254 --> Interactor maps to --> Entity method calls
255 --> Entity produces result
256 --> Interactor maps to --> OrderResponse (DTO)
257 --> Presenter maps to --> ViewModel or JSON
258--> HTTP Response
259```
260 
261Each transformation is a boundary crossing. Each boundary is an opportunity to decouple.
262 
263## Keeping Use Cases Focused
264 
265### One Use Case, One Operation
266 
267Each Use Case should represent a single application operation. If you find a Use Case doing multiple things, split it.
268 
269**Signs of an unfocused Use Case:**
270- The class name contains "And" (e.g., `CreateAndNotifyOrder`)
271- The execute method has conditional branches for fundamentally different operations
272- The class has more than 3-4 dependencies
273- The test file has tests for unrelated scenarios
274 
275### Use Case Granularity Guidelines
276 
277| Granularity | Use Case Example | Notes |
278|-------------|-----------------|-------|
279| **Too coarse** | `ManageOrders` | Does everything -- create, update, cancel, refund |
280| **Right level** | `PlaceOrder`, `CancelOrder`, `RefundOrder` | Each is a single operation with clear input and output |
281| **Too fine** | `ValidateOrderItems`, `CalculateOrderTotal` | These are steps within a use case, not standalone operations |
282 
283### Composing Use Cases
284 
285Sometimes one application operation involves multiple steps that could be their own use cases. Two approaches:
286 
287**1. Use Case calls Use Case (simple composition):**
288 
289```python
290class PlaceOrderAndSendConfirmation:
291 def __init__(self, place_order: PlaceOrderInput, send_confirmation: SendConfirmationInput):
292 self._place_order = place_order
293 self._send_confirmation = send_confirmation
294 
295 def execute(self, request: PlaceOrderRequest) -> None:
296 order_result = self._place_order.execute(request)
297 if order_result.is_success:
298 self._send_confirmation.execute(
299 SendConfirmationRequest(order_result.order_id)
300 )
301```
302 
303**2. Domain events (loose coupling):**
304 
305The Use Case emits a domain event; another Use Case subscribes to it. This is better when the steps are truly independent and could happen asynchronously.
306 
307### Use Case Dependencies
308 
309A Use Case should depend on:
310- **Entity types** (to call business rules)
311- **Repository interfaces** (to load and persist entities)
312- **Output port interfaces** (to present results)
313- **Domain service interfaces** (for cross-entity business operations)
314 
315A Use Case should NOT depend on:
316- **Framework types** (HTTP, ORM, message queue)
317- **Concrete infrastructure classes** (database client, email service)
318- **Other use case concrete classes** (use input port interfaces instead)
319- **Configuration or environment variables** (inject configuration as constructor parameters)
320 
321### Testing Use Cases
322 
323Use Cases should be the most thoroughly tested part of the system because they contain the application's automation rules.
324 
325```python
326def test_place_order_calculates_total_and_saves():
327 # Arrange
328 mock_repo = MockOrderRepository()
329 mock_presenter = MockPlaceOrderPresenter()
330 interactor = PlaceOrderInteractor(mock_repo, mock_presenter)
331 
332 request = PlaceOrderRequest(
333 customer_id="cust-1",
334 items=[OrderItemRequest("prod-1", quantity=2, unit_price="25.00")],
335 shipping_address=AddressRequest("123 Main", "Springfield", "62704"),
336 )
337 
338 # Act
339 interactor.execute(request)
340 
341 # Assert
342 assert mock_repo.saved_order is not None
343 assert mock_repo.saved_order.calculate_total() == Money("54.00") # 50 + 4 tax
344 assert mock_presenter.success_response.order_id == mock_repo.saved_order.id
345```
346 
347No database. No web server. No framework. Just the use case logic running in a plain unit test. This is the payoff of the Dependency Rule.
348 

Discussion