@lievendoclo#DevoxxPL
Platinum Sponsor:
Implementing Clean
Architecture
Lieven DOCLO
ACA IT Solutions
@lievendoclo#DevoxxPL
• Senior Java Engineer @ ACA
• Software craftsman
• Design and architecture afficionado
Who am I?
www.insaneprogramming.be
@lievendoclo#DevoxxPL
What is this about?
@lievendoclo#DevoxxPL
Clean Architecture?
@lievendoclo#DevoxxPL
• Hexagonal
• Onion
• Screaming
It’s a mixed bag of goodies
@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
#DevoxxPL @lievendoclo
#DevoxxPL @lievendoclo
#DevoxxPL @lievendoclo
#DevoxxPL @lievendoclo
#DevoxxPL @lievendoclo
@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;
}
@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();
}
}
@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;
}
@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()));
}
@lievendoclo#DevoxxPL
• Freedom to choose your exposure
• REST
• JSON-RPC
• SOAP
• Messaging solutions
• …
Exposing the use case
@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);
}
}
#DevoxxPL @lievendoclo
@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);
}
@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);
}
}
@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”);
}
}
@lievendoclo#DevoxxPL
@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;
}
@lievendoclo#DevoxxPL
• Once again, complete freedom
• MongoDB
• JPA
• JDBC
• Kafka
• Layering
• Caching
• Calling other API’s (REST or other)
Implementing the gateway
#DevoxxPL @lievendoclo
@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
@lievendoclo#DevoxxPL
@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
@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
@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
@lievendoclo#DevoxxPL
Using modular approach
@lievendoclo#DevoxxPL
• Using Kotlin
• Going reactive
• Use event sourcing
Getting all fancy in 2017
interface CreateBuilding {
fun create(request: Mono<CreateBuildingRequest>):
Mono<CreateBuildingResponse>
}
interface GetBuildings {
fun list(): Flux<BuildingResponse>
}
@lievendoclo#DevoxxPL
• Clean Architecture can be evolutionary
• Start off from use cases
• Services
• Go up or down
• Fit for purpose
What about legacy projects?
#TDD
@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?
@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
#DevoxxPL @lievendoclo
#DevoxxPL @lievendoclo
#DevoxxPL @lievendoclo
https://github.com/lievendoclo/cleanarch

Clean architecture

  • 1.
  • 2.
    @lievendoclo#DevoxxPL • Senior JavaEngineer @ ACA • Software craftsman • Design and architecture afficionado Who am I? www.insaneprogramming.be
  • 3.
  • 4.
  • 5.
    @lievendoclo#DevoxxPL • Hexagonal • Onion •Screaming It’s a mixed bag of goodies
  • 6.
    @lievendoclo#DevoxxPL • Core conceptsbeen 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
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
    @lievendoclo#DevoxxPL The use cases @FunctionalInterface @Boundary publicinterface 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 usecase 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 usecase 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 usecase 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())); }
  • 16.
    @lievendoclo#DevoxxPL • Freedom tochoose your exposure • REST • JSON-RPC • SOAP • Messaging solutions • … Exposing the use case
  • 17.
    @lievendoclo#DevoxxPL You can alreadytry 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); } }
  • 18.
  • 19.
    @lievendoclo#DevoxxPL Creating the domain @Value publicclass 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 realuse 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 cantry 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”); } }
  • 22.
  • 23.
    @lievendoclo#DevoxxPL Implementing the gateway @Named publicclass 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
  • 25.
  • 26.
    @lievendoclo#DevoxxPL • A lotof data structures for a simple write • Building • CreateBuildingRequestModel • CreateBuildingJsonPayload • BuildingDocument • When adding reads, it gets worse • BuildingResponseModel • JsonBuilding Decoupling has a price
  • 27.
  • 28.
    @lievendoclo#DevoxxPL • You canmake 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 canmake 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 arenot 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
  • 31.
  • 32.
    @lievendoclo#DevoxxPL • Using Kotlin •Going reactive • Use event sourcing Getting all fancy in 2017 interface CreateBuilding { fun create(request: Mono<CreateBuildingRequest>): Mono<CreateBuildingResponse> } interface GetBuildings { fun list(): Flux<BuildingResponse> }
  • 33.
    @lievendoclo#DevoxxPL • Clean Architecturecan be evolutionary • Start off from use cases • Services • Go up or down • Fit for purpose What about legacy projects? #TDD
  • 34.
    @lievendoclo#DevoxxPL • More readablecode • 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 littleframeworks 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
  • 36.
  • 37.
  • 38.