Once your system is properly layered, a new problem appears:
Some responsibilities do not belong to a single layer, but still affect the entire system.
These are called cross-cutting concerns.
What are Cross-Cutting Concerns?
They are features that:
- span multiple layers
- affect many classes or services
- are not part of core business logic
Examples:
- logging
- error handling
- retry mechanisms
- monitoring
- authentication
- caching (sometimes)
Why They Are a Problem
If handled incorrectly, they lead to:
- duplicated code everywhere
- cluttered business logic
- hard-to-maintain services
- inconsistent behavior
Bad Design: Mixing Concerns Everywhere
class OrderService:
def place_order(self):
print("LOG: placing order")
try:
# business logic
pass
except Exception as e:
print("LOG: error occurred")
Problems:
- logging repeated in every service
- retry logic scattered
- business logic polluted
Good Design: Separation of Concerns
Cross-cutting logic should be handled separately from core business logic.
1. Logging as a Separate Layer
Instead of writing logs everywhere:
- use logging utilities
- or interceptors
- or decorators
Example
def log(func):
def wrapper(*args, **kwargs):
print("LOG: start")
result = func(*args, **kwargs)
print("LOG: end")
return result
return wrapper
2. Retry Mechanism
Retry logic should not be inside business services.
It should be abstracted.
Example
def retry(func):
def wrapper(*args, **kwargs):
for _ in range(3):
try:
return func(*args, **kwargs)
except:
pass
return wrapper
3. Monitoring & Observability
Monitoring should:
- track system behavior
- not interfere with business logic
It runs alongside execution, not inside it.
Key Insight
Cross-cutting concerns should surround business logic, not mix with it.
How to Handle Cross-Cutting Concerns
There are multiple approaches:
1. Decorators (simple systems)
Wrap behavior externally.
2. Middleware (web systems)
Intercept requests and responses.
3. Aspect-Oriented Design (advanced systems)
Inject behavior at runtime.
Real System Example
Order Service Without Clean Separation
- logging inside service
- retry inside service
- error handling repeated
Order Service With Clean Separation
- service handles only business logic
- logging handled externally
- retry handled by wrapper
- monitoring handled by system layer
Why This Matters in LLD
Without proper separation:
- services become bloated
- logic becomes unreadable
- debugging becomes complex
With proper separation:
- business logic stays clean
- system behavior is consistent
- scaling becomes easier
Design Principle
Cross-cutting concerns should be handled outside core business logic to keep systems clean and maintainable.
One-Line Takeaway
Logging, retry, and monitoring should wrap around business logic, not live inside it.
Top comments (0)