Cross-Cutting Concerns in Clean Architecture
Fast recall
Quick revision
Core answer
@Configuration, or via AOP — not inside use cases or entities. Use Spring AOP / interceptors for logging, metrics, and tracing. Use Spring Security at the web adapter for authentication and edge authorization. Use Spring's `@Transactional` on the use case implementation (or wrap interactors with a transactional proxy) for atomicity. The principle: business logic stays framework-free; cross-cutting infrastructure stays in the outer rings, woven in transparently.Where cross-cutting concerns belong in a Clean Architecture project, how AOP/interceptors satisfy them without polluting the inner layers, and the pragmatic compromises (e.g., @Transactional on the interactor).
Adding @Transactional / @PreAuthorize / Logger calls inside use cases, defeating the framework-free goal.
Discuss authorization layers (edge vs domain), distributed tracing across adapters, and the difference between policy (where) and enforcement (how) for cross-cutting concerns.
Interview-ready guide
Interview answer
The principle
Cross-cutting concerns appear everywhere: logging, metrics, tracing, authentication, authorization, transactions, auditing, rate limiting, caching, validation.
Clean Architecture's rule: inner layers shouldn't know about infrastructure. Don't put Logger.info in use cases. Don't annotate use cases with @PreAuthorize. Don't put @Transactional inside entity methods.
Instead: handle at the boundary (controllers, adapters, configuration) or weave in transparently via AOP/interceptors. Use case stays focused on business logic.
Logging — AOP at the boundary
Don't sprinkle log.info inside business logic. AOP aspect at execution(* com.example.application.service...(..)). Around advice logs USECASE_START, duration, USECASE_END, errors.
Use cases log-free, just business logic. Logging policy in one place, consistent across all use cases. Adapters have their own logging for adapter-specific concerns (HTTP, DB query timing).
Inside the domain — if logging an event is part of the business rule (audit), emit a domain event instead of calling a logger.
Metrics and tracing — OpenTelemetry/Micrometer
Same approach: instrument at boundary. AOP aspect for use case timer with name + status tag.
Distributed tracing: adapters create/propagate spans (HTTP middleware automatic, Kafka consumer extracts trace context from headers). OpenTelemetry auto-instrumentation handles JDBC, HTTP client. Use case is inside a span automatically; never imports OpenTelemetry.
Authentication — Spring Security at the web adapter
Authentication is a web-adapter concern. Spring Security validates JWT/session, builds Principal.
Controller extracts user ID from Spring Security context and passes to use case as plain string. Use case sees only customerId: String — doesn't know JWT, session, or Spring Security.
Authorization — edge vs domain
Two layers:
Edge/endpoint-level (web adapter): coarse role-based, 'only admins can call /admin/*'. Spring Security @PreAuthorize on controller. Stops unauthenticated/wrong-role at the door.
Domain/fine-grained (entity/use case): 'a user can cancel only their own order'. Belongs in the entity as explicit business rule. cancel(requestingUserId) checks customerId.equals(requestingUserId).
Why split: edge security is policy (can this caller hit this URL?). Domain authorization is business rule (can this user perform this action on this object?). Mixing in one annotation hides intent.
Transactions — at the use case boundary
User goal = business transaction. Use case is the natural boundary.
Pattern A (pragmatic, common): @Transactional on the interactor implementation. Mild Spring contamination but only one annotation, in implementation file, not port interface.
Pattern B (purer): wrap interactor with transactional proxy in @Configuration. Interactor stays Spring-free, transactional behavior added by configuration.
Pattern C (purist): UnitOfWork interface in inner layer, Spring-backed implementation in adapter. Use case explicitly uses unitOfWork.execute(() → ...). Heaviest, cleanest.
Most teams pick Pattern A. Boundary stays at the use case — that's the architectural goal.
Auditing, caching, validation
Auditing: AOP aspect on use cases logs (user, action, timestamp, result) to audit table. Or domain emits OrderCancelled event, adapter writes to audit.
Caching: @Cacheable in adapter implementation of a query port, not on use case. Use case asks the port; adapter has its caching strategy.
Validation: HTTP-level (JSR-303 @Valid, @NotBlank) at controller for malformed requests. Business validation (must own order, item must be in stock) inside entity/use case. Two layers, two purposes.
Common mistakes
- @Transactional on controller — wrong layer, use case is the right boundary
- @PreAuthorize on use case — couples to Spring Security; controller for edge, plain Java check in entity for domain auth
- Manual logging at every step in use case — use AOP, log at boundaries
- Caching inside use case — belongs in adapter
- Business rules in controller — belongs in entity/use case
- AOP for everything — some concerns are simple enough to live as explicit calls
Recommendation
Keep cross-cutting concerns out of use cases and entities. Put them at the adapter boundary (Spring Security, request validation in controllers; caching, retries in repository adapters), in @Configuration (transactional proxies), or via AOP aspects (logging, metrics, tracing, auditing). For authorization, split: coarse role-based at the web adapter, fine-grained business rules inside the entity. Put @Transactional on the use case implementation (pragmatic) or wrap via configuration (purer). The principle is consistent: business logic stays framework-free; infrastructure is woven in transparently from the outer rings.
Cross-Cutting Concerns in Clean Architecture
Direct Answer
Cross-cutting concerns in Clean Architecture should live at the boundary — in the adapter layer, in @Configuration, or via AOP — not inside use cases or entities. Use Spring AOP / interceptors for logging, metrics, and tracing. Use Spring Security at the web adapter for authentication and edge authorization. Use Spring's @Transactional on the use case implementation (or wrap interactors with a transactional proxy) for atomicity. The principle: business logic stays framework-free; cross-cutting infrastructure stays in the outer rings, woven in transparently.
Common Mistakes & Pitfalls
Adding @Transactional / @PreAuthorize / Logger calls inside use cases, defeating the framework-free goal.
Check your understanding
Answer these without looking back at the article.
- Walk through how you'd add distributed tracing without modifying the use case.
- When does @PreAuthorize on the controller suffice, and when do you need domain-level authorization?
- How do you implement audit logging without polluting the domain?
- Why is caching better in the adapter than in the use case?
Related interview prep categories
1Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.