Return to site

🧩🚀 MICROSERVICES WITH JAKARTA EE: CLIENT + CONTROLLER/SERVICE/DAO (WITH CODE)

· jakartaee

🔸 TLDR 🧠

▪️ Use Jakarta EE standards to build microservices cleanly: Client (JAX-RS Client) → Controller (JAX-RS) → Service (@Transactional) → DAO (JPA).

Section image

🔸 THE IDEA

▪️ Build one microservice as a “frontend/BFF” that calls other services (still Java, still Jakarta EE).

▪️ Build one backend microservice with clean layers: Controller (JAX-RS) → Service → DAO (JPA).

▪️ Benefit: clear boundaries, testable code, and standard APIs (jakarta.*) ✨

🔸 FRONTEND: MICROSERVICE CLIENT CODE (CALLING THE BACKEND) 🌐

Example: a “frontend” microservice calls customer-service via the Jakarta REST (JAX-RS) Client API.

package com.example.frontend.client;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;

import java.time.Duration;

@ApplicationScoped
public class CustomerApiClient {

    private final Client client;
    private final Jsonb jsonb = JsonbBuilder.create();

    public CustomerApiClient() {
        this.client = ClientBuilder.newBuilder()
                // vendor-specific config often lives here (timeouts, TLS, etc.)
                .build();
    }

    public CustomerDto getCustomer(long id) {
        WebTarget target = client
                .target("http://customer-service:8080")
                .path("/api/customers/{id}")
                .resolveTemplate("id", id);

        String json = target
                .request(MediaType.APPLICATION_JSON)
                .get(String.class);

        return jsonb.fromJson(json, CustomerDto.class);
    }

    // DTO kept simple for the “frontend/BFF” boundary
    public record CustomerDto(long id, String name, String email) {}
}

✅ Why this approach

▪️ No magic: standard JAX-RS client

▪️ Easy to wrap with caching, retries, auth headers, correlation IDs 🔐📌

▪️ Keeps “frontend orchestration” out of the domain service

🔸 BACKEND: CONTROLLER + SERVICE + DAO (JAKARTA EE LAYERS) 🏗️

✅ 1) Controller (JAX-RS resource) — HTTP boundary

package com.example.customer.api;

import com.example.customer.service.CustomerService;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

@Path("/api/customers")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CustomerResource {

    @Inject
    CustomerService service;

    @GET
    @Path("/{id}")
    public CustomerDto get(@PathParam("id") long id) {
        return service.getById(id)
                .map(c -> new CustomerDto(c.getId(), c.getName(), c.getEmail()))
                .orElseThrow(() -> new NotFoundException("Customer " + id + " not found"));
    }

    @POST
    public Response create(@Valid CreateCustomerRequest req) {
        var created = service.create(req.name(), req.email());
        return Response.status(Response.Status.CREATED)
                .entity(new CustomerDto(created.getId(), created.getName(), created.getEmail()))
                .build();
    }

    public record CustomerDto(long id, String name, String email) {}

    public record CreateCustomerRequest(
            @NotBlank String name,
            @NotBlank String email
    ) {}
}

✅ 2) Service — business rules + transaction boundary

package com.example.customer.service;

import com.example.customer.persistence.Customer;
import com.example.customer.persistence.CustomerDao;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;

import java.util.Optional;

@ApplicationScoped
public class CustomerService {

    @Inject
    CustomerDao dao;

    public Optional<Customer> getById(long id) {
        return dao.findById(id);
    }

    @Transactional
    public Customer create(String name, String email) {
        // business rules live here ✅
        var customer = new Customer();
        customer.setName(name.trim());
        customer.setEmail(email.trim().toLowerCase());
        dao.persist(customer);
        return customer;
    }
}

✅ 3) DAO — persistence logic (JPA / EntityManager)

package com.example.customer.persistence;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

import java.util.Optional;

@ApplicationScoped
public class CustomerDao {

    @PersistenceContext
    EntityManager em;

    public Optional<Customer> findById(long id) {
        return Optional.ofNullable(em.find(Customer.class, id));
    }

    public void persist(Customer customer) {
        em.persist(customer);
    }
}

✅ 4) Entity — your database model

package com.example.customer.persistence;

import jakarta.persistence.*;

@Entity
@Table(name = "customers")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 120)
    private String name;

    @Column(nullable = false, unique = true, length = 180)
    private String email;

    public Long getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }

    public void setName(String name) { this.name = name; }
    public void setEmail(String email) { this.email = email; }
}

🔸 TAKEAWAYS ✅

▪️ Jakarta EE gives you “batteries included” building blocks: CDI, JAX-RS, JSON-B, Bean Validation, JPA, Transactions ⚙️

▪️ Keep boundaries sharp: HTTP in the controller, rules in the service, SQL/JPA in the DAO 🧱

▪️ For microservice-to-microservice calls, wrap the client: add timeouts, headers, tracing, retries (where appropriate) 🔐⏱️

▪️ This layering makes code easier to test, refactor, and scale with teams 📈

#JakartaEE #Microservices #Java #JAXRS #CDI #JPA #REST #Backend #SoftwareArchitecture #CleanCode #CloudNative

Go further with Java certification:

Java👇

Spring👇

SpringBook👇

JavaBook👇