Wenn du zum ersten Mal eine Java-EE-Anwendung auf WildFly deployest, wirkt die Struktur oft wie ein Detail, das man „später“ aufräumt. Es läuft ja auch, wenn du alles in eine WAR packst. Genau deshalb stolpern viele erst dann über das Thema, wenn die Anwendung wächst oder wenn du dieselbe Logik an mehreren Stellen brauchst.

Stell dir deine Anwendung als zwei Schichten vor. Oben sitzt alles, was mit HTTP zu tun hat: REST, JSON, Auth, Statuscodes, typische Web-Fehler. Darunter sitzt das, was fachlich passiert: Regeln, Transaktionen, Datenkonsistenz, Nebenwirkungen wie „jetzt wird wirklich gespeichert“. In Java EE ist es üblich, diese Schichten bewusst zu trennen: Die Web-Schicht kommt als WAR, die fachliche Schicht als EJB-Modul (meist ein EJB-JAR).

Das ist keine Pflichtübung und auch kein Selbstzweck. Die Trennung hilft dir, Verantwortung sauber zu schneiden. Deine Web-Schicht bleibt dünn und bleibt auf Transport fokussiert. Deine Business-Logik bleibt unabhängig davon, ob sie gerade über einen REST-Endpoint, eine interne Admin-Oberfläche oder später vielleicht über einen anderen Einstiegspunkt aufgerufen wird. Genau hier entsteht Wiederverwendung: Nicht durch Copy-Paste, sondern dadurch, dass du fachliche Bausteine baust, die nicht an eine einzelne Oberfläche gekoppelt sind.

 

Was genau bringt dir die Trennung von EJB und WAR?

Der erste Vorteil ist Verantwortung: In der WAR geht es um Request/Response, DTOs, Validierung von Eingaben, Security-Kontext und Fehlerbilder, die ein Client verstehen kann. Im EJB geht es um Fachlichkeit, Transaktionen und Datenkonsistenz. Das klingt theoretisch, wird aber sehr praktisch, sobald du Fehler sauber behandeln willst: Ein HTTP-Problem (falscher Request, fehlendes Token, falsches Media-Type) hat nichts in deiner Business-Logik verloren. Umgekehrt sollte ein fachlicher Fehler (z. B. „Kunde ist gesperrt“) nicht als „500 Internal Server Error“ durchrutschen, nur weil irgendwo ein Servlet eine Exception nicht richtig mapped.

Der zweite Vorteil ist Stabilität durch klare Schnittstellen. Wenn du Business-Logik als EJB hinter ein Interface packst, ist das Interface deine Vereinbarung. Deine Web-Schicht kennt dann nur diese Signatur und nicht, wie du intern Dinge löst. Das macht Refactorings sicherer: Du kannst intern ein Repository austauschen, eine Query optimieren oder Transaktionsgrenzen anpassen, ohne in jeder Web-Klasse „mitzuschrauben“.

Der dritte Vorteil ist Deployment-Flexibilität. Auf WildFly kannst du EJBs als eigenes JAR deployen und ein oder mehrere WARs darauf zugreifen lassen. Alternativ kannst du beides in ein EAR packen, wenn du bewusst ein gemeinsames Release bündeln willst. Beides ist valide. Entscheidend ist: Durch die Trennung hast du überhaupt erst die Wahl. Ohne Trennung bleibt dir nur „alles zusammen“.

Und dann kommt der Punkt, der in der Praxis oft der Ausschlag ist: Wiederverwendbarkeit. Ein EJB-JAR ist ein Modul, das du als Abhängigkeit und/oder als Deployment nutzen kannst. Du kannst denselben fachlichen Service in einer Admin-WAR, einer öffentlichen API-WAR und vielleicht später in einem Batch-Job einsetzen, ohne die Logik zu duplizieren.

 

Wiederverwendung in der Praxis: Ein EJB als fachlicher Baustein

Für Wiederverwendung brauchst du zwei Dinge: eine stabile API (Interface) und eine klare Abhängigkeit in deine WAR.

Ein kleines, realistisches Beispiel: Du hast einen Service, der Bestellungen freigibt. Das ist Fachlogik, also EJB. Deine Web-Schicht ruft diesen Service auf, egal ob der Trigger von einem REST-Endpoint oder einer internen Oberfläche kommt.

// im EJB-JAR
@Local
public interface OrderService {
    void approve(long orderId);
}

@Stateless
public class OrderServiceBean implements OrderService {
    @Override
    public void approve(long orderId) {
        // fachliche Checks + DB-Änderungen
    }
}

In der WAR konsumierst du nur das Interface. Auf Java-EE-Servern wie WildFly kannst du das per Injection machen:

// in der WAR
@Path("/orders")
public class OrderResource {
    @EJB
    private OrderService orderService;

    @POST
    @Path("/{id}/approve")
    public void approve(@PathParam("id") long id) {
        orderService.approve(id);
    }
}

Wichtig ist hier nicht die Annotation, sondern die Richtung: Die WAR hängt von der Business-API ab, nicht andersherum. Das EJB weiß nichts von HTTP, Paths oder JSON. Dadurch kannst du denselben Service auch aus einem zweiten WAR oder einem Timer heraus verwenden, ohne ihn umzubauen.

Wenn du EJB-JAR und WAR getrennt deployst, kommt häufig JNDI ins Spiel, weil dein WAR den Bean-Namen zur Laufzeit auflösen muss. WildFly kann viele Fälle trotzdem über @EJB lösen, aber sobald mehrere Deployments beteiligt sind oder du explizite Namen brauchst, wird es konkreter. Das ist kein Drama, nur ein Detail der Laufzeitverkabelung. Für den Einstieg ist die Kernaussage wichtiger: Die Schnittstelle bleibt dieselbe.

 

Maven, Abhängigkeiten und Versionen: Damit Wiederverwendung nicht wehtut

Wiederverwendung funktioniert nur dann gut, wenn du sie sauber in dein Build integrierst. Mit Maven ist das in der Regel ein Multi-Module-Projekt:

  • Parent-Projekt (packaging pom)
  • Modul myapp-ejb (packaging ejb oder jar)
  • Modul myapp-web (packaging war)

Das Web-Modul hängt vom EJB-Modul ab, typischerweise auf das Interface und gemeinsame DTOs. In WildFly-Umgebungen ist es üblich, Java-EE-APIs nicht ins WAR zu packen, sondern als „provided“ zu deklarieren, weil der Server sie ohnehin liefert.

Spannend wird es bei Versionen. Sobald du dein EJB-JAR wiederverwenden willst, brauchst du eine Versionsstrategie: Welche Änderungen sind kompatibel, welche nicht? Wenn du eine Methode am Interface umbenennst oder Parameter änderst, brechen alle Konsumenten. Das ist nicht „EJB-spezifisch“, das ist ganz normales API-Design. Trennung macht diese Brüche nur sichtbarer, und das ist gut.

Praktische Leitlinien, die dir Ärger sparen:

  • Halte deine EJB-Interfaces klein und fachlich klar. Weniger Methoden bedeuten weniger Kopplung.
  • Ändere Signaturen selten. Wenn du erweitern musst, füge lieber neue Methoden hinzu und lasse alte (vorerst) bestehen.
  • Packe web-spezifische Klassen nicht ins EJB-JAR. Keine javax.ws.rs.*, keine Servlet-APIs, keine JSON-Mapping-Klassen.

In IntelliJ merkst du den Vorteil schnell: Du kannst in einem Modul fokussiert arbeiten, Navigation und Refactorings sind sauberer, und du erkennst Abhängigkeiten sofort.

 

Fazit

EJB und WAR zu trennen ist kein akademischer Stil, sondern ein Hebel für Wartbarkeit und Wiederverwendung. Die WAR bleibt die dünne Schicht für HTTP und Präsentation, das EJB-Modul kapselt Fachlogik, Transaktionen und Datenkonsistenz. Sobald du mehrere Oberflächen, mehrere Deployments oder schlicht mehr Lebensdauer im Projekt hast, wirst du froh sein, wenn die Business-Logik nicht an eine einzelne Web-App gebunden ist.

Wenn du mit WildFly arbeitest, hast du außerdem eine stabile Plattform, die EJBs genau für diesen Zweck anbietet: wiederverwendbare, transaktionale Services im Container. Nutze das, indem du Schnittstellen sauber definierst, Abhängigkeiten in Maven klar ziehst und die Web-Schicht bewusst „dünn“ hältst. Das ist die Art von Struktur, die dich später nicht ausbremst, sondern schneller macht.