# Java Architecture

> Arquitetura Java/Spring Boot: SOLID, DDD, Clean Architecture, monolito modular, event-driven (Kafka), bounded contexts. Use ao estruturar pacotes, criar entidades JPA, aggregates, repositories, use cases Spring, ou projetar comunicação entre módulos.

- Skill: `nxs-cafi/java-architecture` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nxs-cafi/java-architecture`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nxs-cafi/java-architecture/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: nxs-cafi (https://skillmd.com/u/nxs-cafi)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/nxs-cafi/java-architecture

---


# Arquitetura Java — Spring Boot

## Estrutura de Pacotes (Monolito Modular)

```
com.company.{app}/
├── {boundedcontext}/           # ex: order, payment, catalog
│   ├── domain/
│   │   ├── model/              # Entidades, Aggregates, Value Objects
│   │   ├── event/              # Domain Events
│   │   ├── repository/         # Interfaces (ports)
│   │   └── exception/          # DomainException tipadas
│   ├── application/
│   │   ├── service/            # Use Cases (@Service, 1 classe = 1 caso de uso)
│   │   ├── dto/                # Command/Query DTOs
│   │   └── port/               # Ports de infra (EmailPort, PaymentPort)
│   ├── infrastructure/
│   │   ├── persistence/        # JPA entities, Repository impl
│   │   ├── messaging/          # Kafka producers/consumers
│   │   └── config/             # @Configuration do módulo
│   └── interfaces/
│       ├── rest/               # @RestController
│       └── listener/           # @KafkaListener
└── shared/                     # kernel compartilhado (mínimo!)
    ├── exception/
    └── util/
```

**Regra:** módulos não importam `infrastructure` de outros bounded contexts — apenas `application` ports ou eventos.

## Clean Architecture com Spring

```java
// domain/model/Order.java — SEM anotações Spring/JPA no núcleo puro (opção 1)
public class Order {
    private final OrderId id;
    private final CustomerId customerId;
    private OrderStatus status;
    private final List<OrderItem> items = new ArrayList<>();

    public void addItem(OrderItem item) {
        if (status != OrderStatus.PENDING) {
            throw new OrderNotEditableException(id);
        }
        items.add(item);
    }

    public void confirm() {
        if (items.isEmpty()) throw new EmptyOrderException(id);
        this.status = OrderStatus.CONFIRMED;
        registerEvent(new OrderConfirmedEvent(id, customerId));
    }
}

// application/service/CreateOrderService.java
@Service
@RequiredArgsConstructor
public class CreateOrderService {
    private final OrderRepository orderRepository;
    private final ApplicationEventPublisher events;

    @Transactional
    public OrderResponse execute(CreateOrderCommand cmd) {
        var order = Order.create(cmd.customerId(), cmd.items());
        orderRepository.save(order);
        order.pullDomainEvents().forEach(events::publishEvent);
        return OrderResponse.from(order);
    }
}

// interfaces/rest/OrderController.java — FINO
@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderController {
    private final CreateOrderService createOrderService;

    @PostMapping
    public ResponseEntity<OrderResponse> create(@Valid @RequestBody CreateOrderRequest req) {
        return ResponseEntity.status(HttpStatus.CREATED)
            .body(createOrderService.execute(req.toCommand()));
    }
}
```

## DDD — Padrões Essenciais

### Value Object (Record)
```java
public record Money(BigDecimal amount, Currency currency) {
    public Money {
        if (amount.compareTo(BigDecimal.ZERO) < 0)
            throw new IllegalArgumentException("Amount cannot be negative");
    }
    public Money add(Money other) {
        if (!currency.equals(other.currency)) throw new CurrencyMismatchException();
        return new Money(amount.add(other.amount), currency);
    }
}
```

### Aggregate + Domain Events
```java
public class Order {
    @Transient
    private final List<DomainEvent> domainEvents = new ArrayList<>();

    protected void registerEvent(DomainEvent event) { domainEvents.add(event); }
    public List<DomainEvent> pullDomainEvents() {
        var events = List.copyOf(domainEvents);
        domainEvents.clear();
        return events;
    }
}
```

### Repository (Port)
```java
// domain/repository/OrderRepository.java
public interface OrderRepository {
    Optional<Order> findById(OrderId id);
    Order save(Order order);
    List<Order> findPendingByCustomer(CustomerId customerId);
}

// infrastructure/persistence/JpaOrderRepository.java
@Repository
@RequiredArgsConstructor
public class JpaOrderRepository implements OrderRepository {
    private final OrderJpaRepository jpa;
    private final OrderMapper mapper;

    @Override
    public Optional<Order> findById(OrderId id) {
        return jpa.findById(id.value()).map(mapper::toDomain);
    }
}
```

## Event-Driven

- **Domain Event** → publicado in-process via `ApplicationEventPublisher`
- **Integration Event** → Kafka após Outbox (ver `~/.cursor/skills/references/event-driven-patterns.md`)
- Consumidores idempotentes (`@KafkaListener` + dedup por event_id)

## Monolito Modular vs Microsserviços

| Critério | Monolito Modular | Microsserviço |
|----------|------------------|---------------|
| Time | < 15 eng, 1 produto | Times autônomos por domínio |
| Deploy | Único artefato | Deploy independente |
| Consistência | Transação local ACID | Saga / eventual consistency |
| Quando | Default — comece aqui | Bounded context maduro + SLA distinto |

## Anti-Patterns

| Anti-Pattern | Problema | Solução |
|---|---|---|
| `@Autowired` field injection | Teste difícil, acoplamento | Constructor injection + `@RequiredArgsConstructor` |
| `@Service` God Object | Viola SRP | 1 use case por classe |
| `Optional` em entidade | Semântica confusa | Nullable explícito ou Value Object |
| Lógica no `@RestController` | Fat controller | Delegar para application service |
| Anemic Domain Model | Regras fora do domínio | Comportamento nas entidades |
| `@Transactional` no controller | Transação errada | Mover para application layer |

## Referências

- `~/.cursor/skills/references/springboot-structure.md`
- `~/.cursor/skills/references/ddd-patterns.md`
- `~/.cursor/skills/references/event-driven-patterns.md`

