6. @lievendoclo#DevoxxPL
• Core concepts been around for decades
• Got popular by the 2012 article by Robert C. Martin
• A lot of video’s onYouTube
It’s been around for a while
12. @lievendoclo#DevoxxPL
The use cases
@FunctionalInterface
@Boundary
public interface CreateBuilding {
String execute(CreateBuildingRequest request);
}
@Value
public class CreateBuildingRequest {
private final String name;
}
@FunctionalInterface
@Boundary
public interface GetBuilding {
BuildingResponseModel execute(GetBuildingRequest request);
}
@Value
public class BuildingResponseModel {
private final String id;
private final String name;
}
@Value
public class GetBuildingRequest {
private final String buildingId;
}
13. @lievendoclo#DevoxxPL
Exposing the use case through REST
@RestController
@RequestMapping("/building")
public class BuildingController {
private final CreateBuilding createBuilding;
public BuildingController(CreateBuilding createBuilding) {
this.createBuilding = createBuilding;
}
@PostMapping
public ResponseEntity create(@RequestBody CreateBuildingJsonPayload payload) {
String id = createBuilding.execute(new CreateBuildingRequest(payload.getName()));
return ResponseEntity.created(URI.create("/building/" + id))
.build();
}
}
14. @lievendoclo#DevoxxPL
Exposing the use case through REST
@GET
@Produces("application/json")
@Path("/{buildingId}")
public BuildingJson get(@PathParam(("buildingId")) String buildingId) {
BuildingResponseModel response = getBuilding.execute(
new GetBuildingRequest(buildingId));
return present(response);
}
public BuildingJson present(BuildingResponseModel buildingResponse) {
return new BuildingJson(buildingResponse.getId(), buildingResponse.getName());
}
@Value
public class BuildingJson {
private final String id;
private final String name;
}
15. @lievendoclo#DevoxxPL
Exposing the use case through REST
@Immutable
public class BuildingResponseModel {
private final String id;
private final String name;
public <T> T present(Function<BuildingResponseModel, T> presentFunction) {
return presentFunction.apply(this);
}
}
@GET
@Produces("application/json")
@Path("/{buildingId}")
public BuildingJson get(@PathParam(("buildingId")) String buildingId) {
BuildingResponseModel responseModel = getBuilding.execute(
new GetBuildingRequest(buildingId));
return responseModel.present(m ->
new BuildingJson(m.getId(), m.getName()));
}
17. @lievendoclo#DevoxxPL
You can already try this out
@Transactional
@Named
public class CreateBuildingMock implements CreateBuilding {
@Override
public String execute(CreateBuildingRequest request) {
return "42";
}
}
@SpringBootApplication
public class Runner {
public static void main(String[] args) {
SpringApplication.run(Runner.class, args);
}
}
19. @lievendoclo#DevoxxPL
Creating the domain
@Value
public class Building {
private final String id;
private final String name;
}
public interface BuildingEntityGateway {
String save(Building building);
Building get(String id);
}
20. @lievendoclo#DevoxxPL
May the real use case please stand up?
@Transactional
@Named
public class CreateBuildingImpl implements CreateBuilding {
private final BuildingEntityGateway buildingEntityGateway;
public CreateBuildingImpl(BuildingEntityGateway buildingEntityGateway) {
this.buildingEntityGateway = buildingEntityGateway;
}
@Override
public String execute(CreateBuildingRequest request) {
Building building = new Building(null, request.getName());
return buildingEntityGateway.save(building);
}
}
21. @lievendoclo#DevoxxPL
Again: you can try this out
@Named
public class MockBuildingEntityGateway implements BuildingEntityGateway {
@Override
public String save(Building building) {
return "42";
}
@Override
public Building get(String id) {
return new Building(id, “Acme Corp HQ”);
}
}
23. @lievendoclo#DevoxxPL
Implementing the gateway
@Named
public class MongoDbBuildingEntityGateway implements BuildingEntityGateway {
private final Datastore datastore;
public MongoDbBuildingEntityGateway(Datastore datastore) {
this.datastore = datastore;
}
public String save(Building building) {
return datastore.save(
new BuildingDocument(building.getId(), building.getName())
).getId();
}
} @Value
public class BuildingDocument {
@Id
private final String id;
private final String name;
}
24. @lievendoclo#DevoxxPL
• Once again, complete freedom
• MongoDB
• JPA
• JDBC
• Kafka
• Layering
• Caching
• Calling other API’s (REST or other)
Implementing the gateway
26. @lievendoclo#DevoxxPL
• A lot of data structures for a simple write
• Building
• CreateBuildingRequestModel
• CreateBuildingJsonPayload
• BuildingDocument
• When adding reads, it gets worse
• BuildingResponseModel
• JsonBuilding
Decoupling has a price
28. @lievendoclo#DevoxxPL
• You can make compromises
• Add JPA annotations on domain
• Add JSON annotations on response
models
• You can expose your use cases through
RPC (decoupling danger)
• Return the domain from the use case
(wouldn’t recommend it tho, decoupling)
• DDD leakage, need to use interfaces on domain
• Your JSON payload extends the use case request model
• Your JSON view model extends the use case response model
Being pragmatic
29. @lievendoclo#DevoxxPL
• You can make bad compromises
• Add JPA and JSON annotations on domain
and use a single data structure
• Exposing your gateways to the
outside world
• “We’re only doing CRUD anyway!”
Being too pragmatic
30. @lievendoclo#DevoxxPL
• Compromises are not a bad thing
• Going pure isn’t always the most efficient way forward
• Perfection isn’t always needed or even wanted
• Nice to strive for though
• They can be an acceptable level of technical debt
• But it’s debt nonetheless
• There may come a day someone will collect
• Hint: keep track compromise decisions
Just remember
34. @lievendoclo#DevoxxPL
• More readable code
• Also for business people!
• More maintainable code
• Every layer is easily testable
• Unit tests
• Integration tests
• System tests
• Modular boundaries more easily enforced
So why use Clean Architecture?
35. @lievendoclo#DevoxxPL
• As little frameworks as possible in inner layers
• Domain logic and business logic, that’s it!
• Choices are made on the outside (infrastructure) layers
• Easy interchangeability
• The main partition does the runtime wiring
• You can choose Spring Boot, Java EE, Dropwizard or OSGi
here
Just remember