Design Skill
Use this skill when writing or refactoring code to enforce clean design principles focused on simplicity, domain alignment, and tactical Domain-Driven Design patterns.
Core Principles
Method Size
- •Keep methods extremely short: 1-5 lines of code
- •If a method exceeds 5 lines, extract sub-methods with expressive names
- •Each method should do one thing at the level of abstraction indicated by its name
- •Only one level of indentation at most
Method Naming
- •Use expressive names that reflect the domain language
- •Names should reveal intent and make the code read like prose
- •Align naming with the ubiquitous language of the domain
- •Avoid technical jargon when domain language is clearer
Class Size
- •Target class size: ~50 lines of code or less
- •Warning threshold: 100 lines of code
- •Classes exceeding 100 LoC are strong candidates for refactoring
- •If a class grows too large, extract related behavior into new classes
Tactical DDD Patterns
- •Prefer value objects over primitive types
- •Encapsulate logic within value objects where it belongs
- •Wrap primitive collections (List, Set, Map, etc.) in value objects when there's logic operating on them
- •Collocate data with behavior - avoid anemic domain models
- •Use entities for objects with identity, value objects for everything else
- •Apply domain patterns: aggregates, repositories, domain services when appropriate
Data and Behavior Collocation
- •Never separate data from the behavior that operates on it
- •Avoid data transfer objects with separate service classes for logic
- •Put validation, transformation, and business rules in the objects that own the data
- •This creates cohesive, self-contained objects that are easier to understand and test
Testability
- •Collocated data and behavior makes test arrangement simpler
- •Value objects are easy to construct and verify in tests
- •Small methods and classes reduce test complexity
- •Domain objects should be testable without infrastructure dependencies
When Reviewing or Writing Code
Always check:
- •Are methods 1-5 lines? If not, extract and name sub-methods
- •Do names reflect the domain? If not, rename using domain language
- •Is the class under 50 lines? Between 50-100 lines? Over 100 lines?
- •Is logic in value objects or scattered in services?
- •Are primitive collections (List, Set, Map) wrapped in value objects?
- •Is data collocated with behavior or separated?
- •Would this be easy to test?
Examples
❌ Poor Design
python
class OrderService:
def process_order(self, order_data):
# 50 lines of validation, calculation, and persistence logic
# Methods are long, logic is not in domain objects
# Data structures passed around without behavior
class Order:
def __init__(self, items: List[Item]):
self._items = items # Primitive collection exposed
def total(self) -> float:
total = 0
for item in self._items:
if item.quantity > 0:
total += item.price * item.quantity
return total # Logic scattered, no value objects
✅ Good Design
python
class Order:
def __init__(self, items: OrderItems, customer: Customer):
self._items = items
self._customer = customer
def total(self) -> Money:
return self._items.total().apply_discount(self._customer.discount())
def place(self) -> PlacedOrder:
return PlacedOrder(self, self.total())
class OrderItems: # Collection wrapper with behavior
def __init__(self, items: List[OrderItem]):
self._items = items
def total(self) -> Money:
return sum(item.subtotal() for item in self._items)
def has_items(self) -> bool:
return len(self._items) > 0
class OrderItem:
def subtotal(self) -> Money:
return self._price.multiply(self._quantity)
class Money:
def apply_discount(self, discount: Discount) -> Money:
return Money(self._amount * (1 - discount.rate()))
Application Strategy
When creating new code:
- •Identify domain concepts and model them as value objects or entities
- •Put behavior with the data it operates on
- •Write very short methods with domain-aligned names
- •Keep classes small and focused
When refactoring existing code:
- •Identify primitives that should be value objects
- •Identify primitive collections that should be wrapped in value objects
- •Move behavior from services into domain objects
- •Extract long methods into well-named short methods
- •Split large classes when they exceed size thresholds