home / skills / emvnuel / skill.md / ddd-da-massa
This skill helps you apply Domain-Driven Design patterns to Jakarta EE apps, balancing cognitive load across controllers, services, entities, and repositories.
npx playbooks add skill emvnuel/skill.md --skill ddd-da-massaReview the files below or copy the command above to add this skill to your agents.
---
name: ddd-da-massa
description: Practical DDD patterns for Jakarta EE web applications with cognitive load distribution. Use when designing controllers, entities, services, or evaluating cohesion and load balance.
---
# DDD da Massa for Jakarta EE
Practical Domain-Driven Design patterns for web applications, applying Cognitive Load Theory to create maintainable code. Based on Alberto Souza's "DDD da Massa".
## Philosophy
> Enable web application development that does what's necessary, has sufficiently flexible design, and where understanding most of the project requires low effort.
## Key Principles
1. **Leverage frameworks** — Don't ignore them, maximize their value
2. **Contextual load limits** — Different components have different limits
3. **Logical responsibility division** — Based on cognitive load, not feelings
4. **100% cohesive components** — Every attribute used by every method
## Contextual Cognitive Load Limits
Different parts of a web application have different complexity budgets:
| Component | Max Points | Rationale |
| ----------------------- | ---------- | ----------------------------------------- |
| **Controller/Resource** | 7 | Handles information flow, must be clear |
| **Domain Service** | 7 | Business flow should be easily understood |
| **Form/Request DTO** | 9 | Transient state with transformation logic |
| **Entity** | 9 | Persistent state with behavior |
| **Repository** | 3 | Framework does the heavy lifting |
| **Infrastructure** | ∞ | Rarely touched, OK to be complex |
| **Configuration** | ∞ | Template, rarely modified |
## Core Patterns
### 1. 100% Cohesive Controllers
Every method in a controller must use ALL injected dependencies.
```java
@Path("/orders/{orderId}/payments")
@RequestScoped
public class OrderPaymentResource {
@Inject
private OrderRepository orders; // Used by all methods
@Inject
private PaymentGateway gateway; // Used by all methods
@Inject
private Event<PaymentEvent> events; // Used by all methods
@POST
@Transactional
public Response processPayment(
@PathParam("orderId") Long orderId,
@Valid PaymentRequest request) {
Order order = orders.findById(orderId)
.orElseThrow(NotFoundException::new);
Payment payment = request.toPayment(order);
PaymentResult result = gateway.process(payment);
events.fire(new PaymentEvent(order, result));
return Response.ok(PaymentResponse.from(result)).build();
}
}
// All 3 dependencies used ✓
```
### 2. Form Value Objects (Smart DTOs)
DTOs that know how to convert themselves to domain objects.
```java
public record CreateOrderRequest(
@NotBlank String customerId,
@NotEmpty List<@Valid OrderItemRequest> items,
String notes
) {
// Conversion logic lives HERE, not in a separate Mapper
public Order toEntity(Customer customer) {
return new Order(
customer,
items.stream().map(OrderItemRequest::toEntity).toList(),
notes
);
}
}
```
### 3. Rich Entities
Entities hold business logic, not just data.
```java
@Entity
public class Bolao {
private Instant expiresAt;
@ElementCollection
private Set<String> invitedEmails;
// ✓ Logic on state lives in the entity
public Participation accept(User participant) {
if (expiresAt.isBefore(Instant.now())) {
throw new InvitationExpiredException();
}
if (!invitedEmails.contains(participant.getEmail())) {
throw new NotInvitedException(participant);
}
return new Participation(this, participant);
}
}
```
### 4. Domain Service Controllers
When controller + service roles merge (for simple flows):
```java
@Path("/payments/pagseguro/{orderId}")
@RequestScoped
public class PagseguroPaymentCallbackResource {
@Inject
private OrderRepository orders;
@Inject
private PaymentRepository payments;
@Inject
private Event<NewPaymentEvent> paymentEvents;
@POST
@Transactional
public void processCallback(
@PathParam("orderId") Long orderId,
@Valid PagseguroCallbackRequest request) {
Order order = orders.findById(orderId)
.orElseThrow(NotFoundException::new);
Payment payment = request.toPayment(order);
payments.save(payment);
paymentEvents.fire(new NewPaymentEvent(payment));
}
}
// Acts as both controller AND domain service
```
## Load Distribution Check
If entity has LOW load but calling code has HIGH load → bad distribution:
```java
// ❌ Entity too thin, controller too fat
@POST
public Response accept(@Valid AcceptRequest request) {
Bolao bolao = bolaoRepo.findById(request.bolaoId()).get();
// All this logic should be IN the entity
if (bolao.getExpiresAt().isBefore(Instant.now())) {
return Response.status(422).entity("Expired").build();
}
if (!bolao.getEmails().contains(request.email())) {
return Response.status(422).entity("Not invited").build();
}
// ... more external logic
}
// ✓ Move logic to entity
@POST
public Response accept(@Valid AcceptRequest request) {
Bolao bolao = bolaoRepo.findById(request.bolaoId()).get();
User user = userRepo.findByEmail(request.email()).orElseThrow();
Participation participation = bolao.accept(user); // Entity has the logic
participationRepo.save(participation);
return Response.ok().build();
}
```
## When to Create New Classes
Only create new files when:
1. **Cognitive load exceeded** — Must distribute
2. **New domain concept** — Entity, Value Object
3. **Language feature** — Enum, sealed type
Do NOT create classes just because it "feels cleaner".
## Cookbook Index
### Controller Patterns
- [cohesive-resources](cookbook/cohesive-resources.md) - 100% cohesive JAX-RS resources
- [domain-service-controller](cookbook/domain-service-controller.md) - Merged controller/service pattern
### DTO Patterns
- [form-value-objects](cookbook/form-value-objects.md) - Smart DTOs with conversion
- [request-validation](cookbook/request-validation.md) - Validation in DTOs
### Domain Patterns
- [rich-entities](cookbook/rich-entities.md) - Entities with behavior
- [load-distribution](cookbook/load-distribution.md) - Balancing load across layers
### Service Patterns
- [cohesive-services](cookbook/cohesive-services.md) - 100% cohesive CDI services
- [splitting-load](cookbook/splitting-load.md) - When and how to split services
This skill provides practical Domain-Driven Design patterns for Jakarta EE web applications, applying cognitive load distribution to keep code maintainable and easy to understand. It focuses on designing controllers, entities, services, and DTOs so that each component stays within a clear complexity budget and responsibilities are logically divided.
The skill inspects component responsibilities and suggests refactors when cognitive load limits are exceeded. It enforces 100% cohesion (every injected dependency used by every method), promotes smart DTOs that convert themselves, and pushes behavior into rich entities instead of bloated controllers. It also offers patterns for when controllers should act as lightweight domain services and when to split classes.
What if a repository needs specialized behavior?
Keep repositories small (3-point budget) and push domain logic into entities or domain services; use repositories only for persistence concerns.
When should a controller act as a domain service?
When the flow is simple, stateless, and involves a direct orchestration of repositories and events—merge roles to reduce indirection while respecting cohesion.