📐 Camadas - ecosif-moviments
📋 Visão Geral
O serviço ecosif-moviments segue uma arquitetura em camadas bem definida, seguindo os padrões do Spring Boot.
🏗️ Estrutura de Camadas
┌─────────────────────────────────────────────────────────┐
│ Camada de Apresentação (REST) │
│ │
│ Controllers │
│ • BatchController │
│ • DocumentController │
│ • EntryController │
│ • ConsolidationController │
│ • ... (8 mais) │
│ │
│ Responsabilidades: │
│ • Receber requisições HTTP │
│ • Validação básica de entrada │
│ • Transformar DTOs ↔ JSON │
│ • Retornar respostas HTTP │
└────────────────────┬────────────────────────────────────┘
│
│ usa
▼
┌─────────────────────────────────────────────────────────┐
│ Camada de Aplicação (Services) │
│ │
│ Services │
│ • BatchService │
│ • DocumentService │
│ • EntryService │
│ • ConsolidationService │
│ • ... (25 mais) │
│ │
│ Responsabilidades: │
│ • Lógica de negócio │
│ • Validações complexas │
│ • Orquestração de operações │
│ • Transformação DTO ↔ Entity │
│ • Processamento assíncrono │
└────────────────────┬────────────────────────────────────┘
│
│ usa
▼
┌─────────────────────────────────────────────────────────┐
│ Camada de Persistência (Repositories) │
│ │
│ Repositories │
│ • BatchRepository │
│ • DocumentRepository │
│ • EntryRepository │
│ • ... (29 mais) │
│ │
│ Responsabilidades: │
│ • Acesso a dados │
│ • Queries customizadas │
│ • Operações CRUD │
└────────────────────┬────────────────────────────────────┘
│
│ persiste
▼
┌─────────────────────────────────────────────────────────┐
│ Camada de Dados (Entities) │
│ │
│ ecosif-database (Biblioteca Compartilhada) │
│ • Batch • Document • Entry │
│ • Company • Branch • ChartOfAccounts │
│ • ... (52+ entidades) │
│ │
│ Responsabilidades: │
│ • Mapeamento objeto-relacional │
│ • Validações JSR-303 │
│ • Estrutura de dados │
└────────────────────┬────────────────────────────────────┘
│
│ conecta
▼
┌─────────────────────────────────────────────────────────┐
│ PostgreSQL Database │
│ │
│ • Tabelas contábeis │
│ • Históricos │
│ • Temporárias │
└─────────────────────────────────────────────────────────┘
📦 Detalhamento das Camadas
Camada 1: Controllers (REST)
Localização: io.ecosif.moviments.company.controller
Responsabilidades:
- ✅ Receber requisições HTTP
- ✅ Validar entrada básica (@Valid)
- ✅ Converter JSON ↔ DTO
- ✅ Chamar services apropriados
- ✅ Retornar respostas HTTP padronizadas
- ✅ Tratamento de exceções (via @ExceptionHandler)
Exemplo:
@RestController
public class EntryController {
@Autowired
private EntryService entryService;
@PostMapping("/entry")
public ResponseEntity<EntryDTO> createEntry(@Valid @RequestBody EntryDTO dto) {
EntryDTO created = entryService.create(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}
Características:
- Anotações: @RestController, @RequestMapping, @PostMapping, etc.
- Validação: @Valid para DTOs
- Documentação: @ApiOperation, @ApiResponses (Swagger)
Camada 2: Services (Lógica de Negócio)
Localização: io.ecosif.moviments.company.service
Responsabilidades:
- ✅ Implementar lógica de negócio
- ✅ Validações complexas (regras de negócio)
- ✅ Orquestração de múltiplos repositories
- ✅ Transformação DTO ↔ Entity (ModelMapper)
- ✅ Processamento assíncrono (@Async)
- ✅ Transações (@Transactional)
Exemplo:
@Service
@Transactional
public class EntryService {
@Autowired
private EntryRepository repository;
@Autowired
private DocumentService documentService;
public EntryDTO create(EntryDTO dto) {
// Validações de negócio
validatePartidasDobradas(dto);
validateContaContabil(dto.getContaId());
// Transformar DTO em Entity
Entry entry = modelMapper.map(dto, Entry.class);
// Persistir
Entry saved = repository.save(entry);
// Retornar DTO
return modelMapper.map(saved, EntryDTO.class);
}
}
Características:
- Interface + Implementação (padrão recomendado)
- @Service para identificação
- @Transactional para transações
- @Async para operações assíncronas
Camada 3: Repositories (Acesso a Dados)
Localização: io.ecosif.moviments.company.repository
Responsabilidades:
- ✅ Abstração de acesso a dados
- ✅ Operações CRUD (herdadas de JpaRepository)
- ✅ Queries customizadas (@Query)
- ✅ Especificações complexas (Specification)
Exemplo:
@Repository
public interface EntryRepository extends JpaRepository<Entry, Long> {
List<Entry> findByDocumentId(Long documentId);
@Query("SELECT e FROM Entry e WHERE e.contaId = :contaId AND e.debitcredit = :debitcredit")
List<Entry> findByContaAndType(@Param("contaId") Long contaId,
@Param("debitcredit") String debitcredit);
}
Características:
- Interfaces que estendem JpaRepository
- Spring Data JPA implementa automaticamente
- Queries via métodos ou @Query
Camada 4: DTOs (Data Transfer Objects)
Localização: io.ecosif.moviments.company.dto
Responsabilidades:
- ✅ Transferir dados entre camadas
- ✅ Isolar entidades JPA da API
- ✅ Validações de entrada (@NotNull, @Size, etc.)
- ✅ Serialização JSON
Exemplo:
public class EntryDTO {
@NotNull
private Long documentId;
@NotNull
@Size(max = 5)
private String lancamento;
@NotNull
private String debcre; // D ou C
@NotNull
private Long contaId;
@NotNull
private Double valor;
// Getters e Setters
}
Características: - Classes simples (POJOs) - Validações JSR-303 - Sem anotações JPA - Lombok para reduzir boilerplate
Camada 5: Entities (Modelo de Dados)
Localização: Biblioteca ecosif-database
Responsabilidades: - ✅ Representar tabelas do banco - ✅ Mapeamento objeto-relacional (JPA) - ✅ Validações básicas (JSR-303) - ✅ Estrutura de dados do domínio
Exemplo:
@Entity
@Table(name="ct_lancamento")
@Getter
@Setter
public class Entry {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@NotNull
private Long documentId;
// ...
}
Características:
- Anotações JPA (@Entity, @Table, @Column)
- Validações JSR-303
- Lombok para getters/setters
- Sem lógica de negócio
🔄 Fluxo de Dados
Fluxo: Criar Lançamento
Cliente HTTP
│
▼
Controller (EntryController)
│ Valida entrada (@Valid)
│ Converte JSON → EntryDTO
▼
Service (EntryService)
│ Valida regras de negócio
│ Transforma EntryDTO → Entry (Entity)
│ Valida partidas dobradas
▼
Repository (EntryRepository)
│ Executa SQL via Hibernate
▼
PostgreSQL
│ INSERT INTO ct_lancamento
│
▼
Repository
│ Retorna Entry (Entity)
▼
Service
│ Transforma Entry → EntryDTO
│ Processa/Enriquece dados
▼
Controller
│ Converte EntryDTO → JSON
│ Retorna ResponseEntity
▼
Cliente HTTP
🎯 Princípios de Design
1. Separation of Concerns
Cada camada tem responsabilidade única: - Controller: Apresentação HTTP - Service: Lógica de negócio - Repository: Acesso a dados - Entity: Estrutura de dados
2. Dependency Injection
Todas as dependências são injetadas via @Autowired:
- Controllers dependem de Services
- Services dependem de Repositories
- Services podem depender de outros Services
3. Inversion of Control (IoC)
Spring gerencia o ciclo de vida: - Criação de beans - Injeção de dependências - Gerenciamento de transações
4. Don't Repeat Yourself (DRY)
- Services reutilizáveis
- Repositories genéricos (JpaRepository)
- DTOs compartilhados
📝 Padrões Utilizados
Repository Pattern
Abstração de acesso a dados através de interfaces.
Service Layer Pattern
Camada intermediária para lógica de negócio.
DTO Pattern
Separação entre entidades JPA e DTOs de API.
Dependency Injection
Injeção de dependências via Spring.
Factory Pattern
Criação de entidades complexas (ex: BatchFactory).
🔒 Transações
Nível de Transação
Por padrão, transações são gerenciadas nos Services:
@Service
@Transactional
public class EntryService {
// Todos os métodos são transacionais
}
Propagation
- REQUIRED (padrão): Reutiliza transação existente ou cria nova
- REQUIRES_NEW: Sempre cria nova transação
Rollback
- Rollback automático em exceções não verificadas
@Transactional(rollbackFor = Exception.class)para todas as exceções
🔄 Processamento Assíncrono
Operações longas são executadas assincronamente:
@Service
public class ConsolidationService {
@Async
public CompletableFuture<Void> processConsolidation(ConsolidationDTO dto) {
// Processamento longo
return CompletableFuture.completedFuture(null);
}
}
Configuração: @EnableAsync na classe Application
📊 Mapeamento DTO ↔ Entity
ModelMapper
Usado para converter entre DTOs e Entities:
@Autowired
private ModelMapper modelMapper;
// DTO → Entity
Entry entry = modelMapper.map(entryDTO, Entry.class);
// Entity → DTO
EntryDTO dto = modelMapper.map(entry, EntryDTO.class);
🎯 Boas Práticas
- ✅ Controllers devem ser finos - Apenas receber e retornar
- ✅ Lógica de negócio nos Services - Não no Controller
- ✅ Validações no Service - Além das validações JSR-303
- ✅ Repositories apenas para dados - Sem lógica de negócio
- ✅ DTOs para API - Nunca expor Entities diretamente
- ✅ Transações nos Services - Não nos Controllers
- ✅ Exceções tratadas globalmente -
@ExceptionHandler
Última Atualização: 2025-11-27