Uncle Bob – Payroll Case Study (A full implementation)

This is my implementation of Robert C. Martin’s Payroll-Case-Study learning project presented in his book Agile Software Development, and in his videos on cleancoders.com.

The clean Architecture

The clean architecture separates concerns of the application in a scalable and maintainable way.
Clean architecture systems are:

  • Independent of Frameworks. The architecture does not depend on the existence of some library of feature laden software. This allows you to use such frameworks as tools, rather than having to cram your system into their limited constraints.
  • Testable. The business rules can be tested without the UI, Database, Web Server, or any other external element.
  • Independent of UI. The UI can change easily, without changing the rest of the system. A Web UI could be replaced with a console UI, for example, without changing the business rules.
  • Independent of Database. You can swap out Oracle or SQL Server, for Mongo, BigTable, CouchDB, or something else. Your business rules are not bound to the database.
  • Independent of any external agency. In fact your business rules simply don’t know anything at all about the outside world.

CleanArchitecture-81565aba46f035911a5018e77a0f2d4e

boundary,interactor,entity,delivery_mechanismdatabase

Overview

My architecture is based on hexagonal, and clean architecture.
Payroll - Architecture

  • App contains the Business Entities and the Interactors (Use cases)
  • Ports are the boundaries of the app: interfaces and DTOs.
  • Adapters are the implementations of the ports (boundaries)
    • primary adapters are source of the control (or driving adapters)
    • secondary adapters are target of the control (or drived adapters)

Outside of the adatpers are the frameworks, drivers.
All the dependencies pointing towards the center, and nothing on the opposite way.

Business Rules (Entities)

Entities are not the same as JPA Entities, they are high level business rules, policies, and should be independent from any other concerns.

Employee domain colored

Architecture

suggested by Uncle Bob:

vlcsnap-2015-12-27-03h24m01s474

my implementaton:

Probably this is not a perfect solution, I departed from Uncle Bob’s recommendations at some points.

  • I didn’t separated Contoller and Presenter, because I didn’t wanted to move UI workflows to usecase layer. (opening views, popups, etc)
  • I introduced UI abstraction that encapsulates a view and a controller, to provide a single interface for this relation.
  • I used Visitor pattern many places but I found it’s implementation side effects very cumbesome,
    • so somewhere I just used switch (but that switch is in the main module)
  • I didn’t used builders for data structures (request, response dto­s), so i just put them to the ports layer together with interfaces.
  • I used Guice DI in several modules, but only(!) outside of the Boundaries (Ports).
  • There are Unit Tests only for Entities, and Integration tests (testing usecases) in Main module.
  • I used a proxy mechanism implementing entity gateway for JPA.

Payroll - Classes

Modules

This is the module view of my implementation

Payroll - Modules

RUN

Project PayrollMain -> Main.java:

In Main class it is selectable to run with JPA, or InMemory database. This example has configured JPA with hsqldb, so no database needed to test it.

        Payroll.builder()
            .withDatabaseJPA(JPAPersistenceUnit.HSQL_DB)
            .withBankTransferPortFake()
            .withLoadedTestData()
            .buildGuiAdminSwing()
            .run();

screenshot

Full Source Code is available on GITHUB, or as a download:

Written by Dániel Hári

Dániel Hári is the founder of log4jtester.com, cleancodejava.com. Who is an enthusiastic Java developer who enhances his knowledge by continously searching for best practices and modern technologies. Engaged to clean coding, and honors the engineering profession as releasing quality work.

  • Juan

    Hello Daniel,

    great architecture implementation.

    Mine is similar, but I have always had a doubt with it… why ports are always draw in the boundary of the domain (usecases implementation + entities), and it is said that dependency points inwards (i.e. ports should depend on domain and domain on nothing), whereas the reality is that ports are “inside” (domain depends on ports and ports depends on nothing). Dependency points inwards from adapters to ports but not from ports to domain.

    Another issue wich I’m struggling with is defining a generic search method in the database API with a generic predicate (generic search criteria) and how to convert that generic predicate (which is implementation agnostic) to JPA implementation (I use querydsl predicates). Finally I’ve decided not to put a generic search method, but to put a concrete method with the concrete search criteria in the specific database method for the concrete entity. How do you deal with this?

    Thank you.

    • http://log4jtester.com Dániel Hári

      Hi! Thanks!
      1) Ports: I’m not sure what you mean, in my project Ports are dependent only on Entities (domain), easy to check in pom.xml.
      2) Generic search: I’m not clear what you want exactly, but you can define search methods in Gateway interface, then you can implement for JPA, Memory, case by case. Within JPA implementation you can use Criteria or Hql or whatever JPA tool, or even concrete db queries if you decide to fix the database provider of your JPA implementation, this does not effect the architecture. Of course those methods should be implemented for Memory version also in a different way.
      I guess this answer is not so helpful, I could understand better if you provide some example.

      • Juan

        Hello, thanks for answering. Maybe I didn’t explain myself well. I will try to explain better:

        1) I have adapters (primary and secondary), ports (primary and secondary), and the app itself (the inner of the hexagon). The ports “protect” the app from frameworks, etc. I have a primary port with the usecases API (interfaces defining the usecases of the app). So a primary adapter “calls” the primary port, the app “implements” the usecases API, and the app also “calls” the secondary ports (which are agnostic interfaces for accessing external services, database, etc). The secondary adapters “implement” the secondary ports. So the dependencies are:

        Primary adapter —> Primary Port Secondary Port <— Secondary Adapter

        So adapters depend on ports (which is ok), but ports don't depend on App. App depends on ports, which isn't supposed to be the right thing seeing how they always draw the hexagon (ports in the boundary of the hexagon and app inside). It is supposed that ports depends on the inner of the hexagon.

        I've been thinking that in order to acheive that, I have to move the usecases implementation from the "app" module to the "primary port" module, but that would break the rule that ports are just interfaces with no implementation. On the other side, to make secondary port depends on app, I would have to add another interface to the app , and let the secondary port just "extends" it, but that's a little bit tricky.

        Another "problem" is where to place the entities… if you place them in the app (the inner of the hexagon), like you do, then the secondary adapter depends on the app, and that's not ok because it "jumps" the port (the boundary of the hexagon)… secondary adapter should only depends on secondary port. I've place them in the secondary port, but it isn't correct neither. They should be in the inner, and the secondary port "extends" them (another tricky workaround).

        Oops! wrote too much, another day tell you about generic search, I don't wanna be boring.

        • http://log4jtester.com Dániel Hári

          Dear Juan! I could understand better if you could show some actual code. This is so abstract for me :)

  • Borhan Chowdhury

    hey daniel!, nice article. but I just wonder why did you make the folder structure in each module sooooooo long! https://uploads.disquscdn.com/images/10630e1766782875d4c91cd04017b2c79312a5eef5b754bcf79a5f37daf2dbcd.png

    • http://log4jtester.com Dániel Hári

      Yes, because I prefer tree structuring more than other people. And if you use another IDE, like Eclipse, the empty tree sequence will be flatted so you don’t have to climb it.