See the docker-compose.yml file for more details.
To run Unit Tests:
sbt test
To run Integration Tests we need to run both PostgreSQL
and Redis
:
docker-compose up
sbt it:test
docker-compose down
sbt docker:publishLocal
Our image should now be built. We can check it by running the following command:
> docker images | grep shopping-cart
REPOSITORY TAG IMAGE ID CREATED SIZE
shopping-cart latest 646501a87362 2 seconds ago 138MB
To run our application using our Docker image, run the following command:
docker-compose up
The configured test payment client is a fake API that always returns 200 with a Payment Id. Users are encouraged to make modifications, e.g. return 409 with another Payment Id (you can create one here) or any other HTTP status to see how our application handles the different cases.
This fake API can be modified at: https://beeceptor.com/console/payments
Domain driven design is all about developing a ubiquitous language, which is a language that you can use to discuss your software with business folks (who presumably do not know programming).
DDD is all about making your code expressive, making sure that how you talk about your software materializes in your code. One of the best ways to do this is to keep you domain pure. That is, allow the business concepts and entities to be real things, and keep all the other cruft out. However, HTTP, JDBC, SQL are not essential to domain, so we want to decouple those as much as possible.
In concert with DDD, the Onion Architecture and Hexagonal Architecture from Cockburn give us patterns on how to separate our domain from the ugliness of implementation.
We fit DDD an Onion together via the following mechanisms:
The domain package
The domain package constitutes the things inside our domain. It is deliberately free of the ugliness of JDBC, JSON, HTTP, and the rest.
We use Services
as coarse-grained interfaces to our domain. These typically represent real-world use cases. Often times, you see a 1-to-1 mapping of Services
to R
or HTTP API calls your application surfaces.
Inside of the domain, we see a few concepts:
Service
- the coarse grained use cases that work with other domain concepts to realize your use-casesRepository
- ways to get data into and out of persistent storage. Important: Repositories do not have any business logic in them, they should not know about the context in which they are used, and should not leak details of their implementations into the world.payloads
ormodels
- things likeBrand
,Category
,Item
, etc are all domain objects. We keep these lean (i.e. free of behavior).
The repository package
The repository package is where the ugliness lives. It has JDBC things, and the like.
it contains implementations of our Repositories
. We may have 3 different implementations, an in-memory version, skunk version as well as a doobie version.
The http package It contains the HTTP endpoints that we surface via http4s. You will also typically see JSON things in here via circe
The util package The util package could be considered infrastructure, as it has nothing to do with the domain.
NOTE
All business logic is located in domain
package, every package inside is related to some domain.
Service classes contains high level logic that relate to data manipulation, that means that services MUST NOT implement storage.