Migrate all repos into monorepo context folders
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.
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user