Architekturregeln mit falschem und richtigem Code

Diese Regeln machen die Modulgrenzen praktisch sichtbar. Jede Regel enthaelt Anti-Beispiel, gutes Beispiel und Pruefbefehl.

← Zurueck zum Index

R01. Domain bleibt frameworkfrei

Regel: Domain darf keine Spring, JDBC, AMQP oder Web-Klassen importieren.

Falsch:

import org.springframework.stereotype.Service;

@Service
public class Order { }

Richtig:

public final class Order {
    public void markAccepted() {
        // reine Fachlogik
    }
}

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R02. Application kennt Ports, nicht Adapter

Regel: Use Cases verwenden Interfaces aus ports, keine JdbcTemplate oder RabbitTemplate Klassen.

Falsch:

public class AcceptOrderUseCase {
    private final JdbcTemplate jdbc;
}

Richtig:

public class AcceptOrderUseCase {
    private final OrderRepository orders;
    private final PaymentGateway payments;
}

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R03. Adapter implementieren Ports

Regel: Infrastrukturcode sitzt im adapters-Modul und implementiert fachliche Vertrage.

Falsch:

public interface OrderRepository {
    JdbcTemplate jdbc();
}

Richtig:

public final class JdbcOrderRepository implements OrderRepository {
    private final JdbcTemplate jdbc;
}

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R04. Runtime ist Composition Root

Regel: Spring Boot verdrahtet, aber Domain wird nicht von Spring gesteuert.

Falsch:

@Autowired
private Order order;

Richtig:

@Bean
AcceptOrderUseCase useCase(OrderRepository repo) {
    return new AcceptOrderUseCase(repo);
}

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R05. Outbox statt Dual Write

Regel: DB Update und Event-Erzeugung gehoeren in eine Transaktion.

Falsch:

orders.save(order);
rabbitTemplate.convertAndSend(event);

Richtig:

orders.save(order);
outbox.append(order.id(), "OrderAccepted", payload);

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R06. Security am Rand

Regel: JWT/Keycloak gehoeren in Runtime Security, nicht in Domain.

Falsch:

public void accept(Jwt jwt) {
    if(jwt.hasClaim("role")) ...
}

Richtig:

@PreAuthorize("hasRole('ORDER_ADMIN')")
@PostMapping("/{id}/accept")

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R07. Konfiguration externalisieren

Regel: Credentials gehoeren nicht hart in Java-Code.

Falsch:

String password = "order_pass";

Richtig:

spring.datasource.password=${POSTGRES_PASSWORD}

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R08. Tests passend schichten

Regel: Nicht jeder Fachfall braucht Docker.

Falsch:

class QuantityRuleIT { PostgreSQLContainer<?> db; }

Richtig:

class QuantityRuleTest {
    @Test void rejectsNegativeQuantity() {}
}

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R09. Observability ist Teil des Designs

Regel: Kritische Pfade brauchen Metriken und Health Checks.

Falsch:

catch(Exception e) { return; }

Richtig:

metrics.paymentFailed();
log.warn("payment failed", e);

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

R10. Deployment ist nicht nur YAML

Regel: Manifeste brauchen Runbooks, Checks und Rollback-Idee.

Falsch:

kubectl apply -f all.yaml

Richtig:

kustomize build overlays/prod | kubeconform
kubectl rollout status deploy/order-runtime

Pruefen:

grep -R "org.springframework\|JdbcTemplate\|RabbitTemplate" maven-project/domain/src/main/java || true
mvn -q -DskipTests package

Lernwert: Diese Grenze verhindert, dass ein scheinbar kleines Infrastrukturdetail langfristig die Fachlogik schwer testbar und schwer migrierbar macht.

⌂ Cockpit