Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
238 lines
8.0 KiB
Markdown
238 lines
8.0 KiB
Markdown
# Clean Code & Architektur
|
||
|
||
## Architektur: Ports & Adapters (Hexagonal)
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ Application Core │
|
||
│ ┌─────────────────────────────────┐ │
|
||
│ │ Domain Model │ │
|
||
│ │ (Entities, Value Objects) │ │
|
||
│ └─────────────────────────────────┘ │
|
||
│ ┌─────────────────────────────────┐ │
|
||
│ │ Use Cases / Services │ │
|
||
│ │ (Business Logic, Ports) │ │
|
||
│ └─────────────────────────────────┘ │
|
||
└─────────────────────────────────────────┘
|
||
▲ ▲
|
||
Driving Ports Driven Ports
|
||
│ │
|
||
┌────────┴───────┐ ┌────────┴───────┐
|
||
│ REST API │ │ Repository │
|
||
│ (Adapter) │ │ (Adapter) │
|
||
│ Controller │ │ JPA/JDBC │
|
||
└────────────────┘ └────────────────┘
|
||
```
|
||
|
||
- **Domain**: Reine Business-Logik, keine Framework-Abhängigkeiten
|
||
- **Ports**: Interfaces die Use Cases definieren (inbound) und externe Systeme abstrahieren (outbound)
|
||
- **Adapters**: Implementierungen (REST Controller, JPA Repository, Kafka Consumer)
|
||
|
||
## Separation of Concerns
|
||
|
||
- **Controller/Handler**: Nur Request/Response Mapping, Validierung, keine Business-Logik
|
||
- **Service**: Business-Logik, Orchestrierung, Transaktionen
|
||
- **Repository/DAO**: Datenzugriff, Queries
|
||
- **Model/Entity**: Datenstrukturen, Domain-Logik
|
||
- **DTO**: Daten-Transfer zwischen Schichten (nicht Entity direkt exponieren)
|
||
- **Mapper**: Entity ↔ DTO Konvertierung
|
||
- **Config**: Konfiguration, Beans, Dependency Injection
|
||
|
||
## Exception Handling
|
||
|
||
### Strategie
|
||
|
||
- **Domain Exceptions**: Fachliche Fehler (z.B. `AddressNotFoundException`)
|
||
- **Global Exception Handler**: `@RestControllerAdvice` für einheitliche Error-Responses
|
||
- **Keine generischen Exceptions** werfen (nicht `throw new RuntimeException`)
|
||
- **HTTP Status Codes** korrekt nutzen (400, 404, 409, 422, 500)
|
||
|
||
### Pattern
|
||
|
||
```java
|
||
@RestControllerAdvice
|
||
public class GlobalExceptionHandler {
|
||
|
||
@ExceptionHandler(EntityNotFoundException.class)
|
||
public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException ex) {
|
||
return ResponseEntity.status(404)
|
||
.body(new ErrorResponse("NOT_FOUND", ex.getMessage()));
|
||
}
|
||
|
||
@ExceptionHandler(ConstraintViolationException.class)
|
||
public ResponseEntity<ErrorResponse> handleValidation(ConstraintViolationException ex) {
|
||
return ResponseEntity.status(400)
|
||
.body(new ErrorResponse("VALIDATION_ERROR", ex.getMessage()));
|
||
}
|
||
}
|
||
```
|
||
|
||
## Observability (Logging, Tracing, Metrics)
|
||
|
||
### Logging
|
||
|
||
- **Structured Logging** (JSON) mit SLF4J/Logback
|
||
- Log-Level: ERROR (Fehler), WARN (unerwartetes Verhalten), INFO (Business-Events), DEBUG (Entwicklung)
|
||
- **Correlation-ID** in jedem Log-Eintrag (aus Request-Header oder generiert)
|
||
- Keine sensiblen Daten loggen (Passwörter, Tokens, PII)
|
||
|
||
### Tracing (OpenTelemetry)
|
||
|
||
- **OTEL** für Distributed Tracing (Spring Boot Actuator + Micrometer)
|
||
- Trace-ID und Span-ID in Logs propagieren
|
||
- Externe Calls (DB, HTTP, Kafka) automatisch instrumentiert
|
||
|
||
```yaml
|
||
# application.yml
|
||
management:
|
||
tracing:
|
||
sampling:
|
||
probability: 1.0
|
||
otlp:
|
||
tracing:
|
||
endpoint: http://k8s-monitoring-alloy-receiver.monitoring.svc.cluster.local:4317
|
||
# CNP Grafana Alloy - OTLP gRPC Port 4317
|
||
# Doku: https://cnp.gitpages.tech.rz.db.de/core/docs/cnp/latest/cnp-portfolio/observability/tracing.html
|
||
```
|
||
|
||
### Metrics
|
||
|
||
- Spring Boot Actuator Endpoints (`/actuator/health`, `/actuator/metrics`, `/actuator/prometheus`)
|
||
- Custom Business-Metrics wo sinnvoll (z.B. Bestellungen pro Minute)
|
||
- Prometheus-Format für Grafana-Dashboards
|
||
|
||
### Health Checks
|
||
|
||
```yaml
|
||
management:
|
||
endpoint:
|
||
health:
|
||
show-details: always
|
||
health:
|
||
db:
|
||
enabled: true
|
||
kafka:
|
||
enabled: true
|
||
```
|
||
|
||
## Clean Code Prinzipien
|
||
|
||
- **Single Responsibility**: Eine Klasse/Funktion = eine Aufgabe
|
||
- **DRY**: Keine Code-Duplikation, gemeinsame Logik extrahieren
|
||
- **KISS**: Einfachste Lösung die funktioniert
|
||
- **YAGNI**: Nichts implementieren was nicht gefordert ist
|
||
- **Dependency Injection**: Abhängigkeiten injizieren, nicht selbst erstellen
|
||
- **Interface Segregation**: Kleine, fokussierte Interfaces
|
||
- **Open/Closed**: Offen für Erweiterung, geschlossen für Änderung
|
||
|
||
## Naming
|
||
|
||
- Klassen: PascalCase, Substantive (UserService, OrderRepository)
|
||
- Methoden: camelCase, Verben (findById, createUser, validateInput)
|
||
- Variablen: camelCase, sprechend (userCount statt n, isActive statt flag)
|
||
- Konstanten: UPPER_SNAKE_CASE
|
||
- Packages: lowercase, Singular (controller, service, model)
|
||
|
||
## Methoden
|
||
|
||
- Max 20 Zeilen (Richtwert)
|
||
- Max 3 Parameter (sonst Object/DTO)
|
||
- Keine boolean-Parameter (Split in zwei Methoden)
|
||
- Early Return statt tiefe Verschachtelung
|
||
|
||
## Error Responses (API)
|
||
|
||
Einheitliches Format:
|
||
|
||
```json
|
||
{
|
||
"error": "NOT_FOUND",
|
||
"message": "Address with id 42 not found",
|
||
"timestamp": "2026-05-17T19:00:00Z",
|
||
"path": "/api/addresses/42"
|
||
}
|
||
```
|
||
|
||
## Configuration
|
||
|
||
- **application.yml** für Defaults
|
||
- **application-{profile}.yml** für Stage-spezifisch (dev, iu, prod)
|
||
- Secrets NICHT in application.yml (über Env-Variablen oder SecretProviderClass)
|
||
- **@ConfigurationProperties** statt @Value für typsichere Config
|
||
|
||
## Mapper: MapStruct
|
||
|
||
Entity ↔ DTO Mapping über **MapStruct** (compile-time, kein Reflection):
|
||
|
||
```java
|
||
@Mapper(componentModel = "spring")
|
||
public interface AddressMapper {
|
||
AddressDto toDto(Address entity);
|
||
Address toEntity(AddressDto dto);
|
||
List<AddressDto> toDtoList(List<Address> entities);
|
||
}
|
||
```
|
||
|
||
- Kein manuelles Mapping in Services
|
||
- MapStruct generiert Implementierung zur Compile-Zeit
|
||
- Bei komplexen Mappings: `@Mapping(source = "...", target = "...")`
|
||
|
||
## Lombok
|
||
|
||
Getter, Setter, Builder über **Lombok** (kein Boilerplate):
|
||
|
||
```java
|
||
@Data // Getter + Setter + toString + equals + hashCode
|
||
@Builder // Builder-Pattern
|
||
@NoArgsConstructor // JPA braucht Default-Konstruktor
|
||
@AllArgsConstructor // Für Builder
|
||
@Entity
|
||
public class Address {
|
||
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
|
||
private Long id;
|
||
private String name;
|
||
private String street;
|
||
private String city;
|
||
private String zip;
|
||
}
|
||
```
|
||
|
||
### Konventionen
|
||
|
||
- `@Data` für Entities und DTOs
|
||
- `@Builder` für DTOs (immutable Construction)
|
||
- `@Value` für immutable Value Objects (statt @Data)
|
||
- `@Slf4j` für Logger (statt `private static final Logger log = ...`)
|
||
- `@RequiredArgsConstructor` für Constructor-Injection (statt @Autowired)
|
||
|
||
## Validierung
|
||
|
||
### Schichten
|
||
|
||
1. **Bean Validation** (`@Valid`, `@NotNull`, `@Size`) – Syntaktische Prüfung im Controller
|
||
2. **Business Rules** – Fachliche Validierung im Service/Validator (eigene Schicht)
|
||
3. **Domain Invarianten** – Im Entity selbst (z.B. `@PrePersist`)
|
||
|
||
### Pattern: Separater Validator
|
||
|
||
```java
|
||
@Component
|
||
@RequiredArgsConstructor
|
||
public class OrderValidator {
|
||
|
||
public void validate(OrderRequest request) {
|
||
if (request.getQuantity() > MAX_QUANTITY) {
|
||
throw new BusinessRuleViolationException("Maximale Bestellmenge überschritten");
|
||
}
|
||
// Weitere fachliche Regeln...
|
||
}
|
||
}
|
||
```
|
||
|
||
### Konventionen
|
||
|
||
- Bean Validation für einfache Feld-Prüfungen (nicht null, Format, Länge)
|
||
- Eigener Validator für fachliche Regeln (Abhängigkeiten zwischen Feldern, DB-Lookups)
|
||
- Business-Regeln NICHT im Controller
|
||
- Custom Constraint-Annotations für wiederverwendbare Validierungen
|