DDD Part V

Code Implementation

M Yauri M Attamimi
7 min readJan 28, 2020

I’ve just realized that it’s been almost 2 years passed since I wrote DDD Part IV. I’ve been waiting all these times to write this part. Hopefully, this part will be useful for you, at least for me since i believe that the most effective way to tie your knowledge is by writing and sharing it.

Some of you whom probably have read my previous series on the DDD, might realized that deliberately i changed a bit the story’s sequences. This was because i got few messages asked me to give the code sample [talk is cheap, show me the code ~ Linus Torvalds] :-). Therefore, i’ll keep the ES and CQRS section for the last part. Another thing, I’ll be using go (golang) here since i’ve been working with it lately. The version that i’m currently working on while writing this article is 1.13.4.

So, let’s get started…

For the code implementation, i’m gonna start by implementing some of the functionalities within shopping cart since it’s pretty straightforward. I believe nowadays everyone knows about how it works (at least most of us ever bought something online).

First and foremost i’m gonna show you below the common processes of shopping cart. Note that what i’ll be showing here is restricted to thebounded context of the shopping cart, means we’ll just focus with the process of how the user/buyer choose the item and adding that particular item into his cart, subsequently continue with checkout. As for the billing/payment part, that will be in the bounded context of payment service. (if you’re neither really familiar nor confidence with what bounded context is, i would suggest you to read the first part here.

Common Shopping Cart Process

Worth to mention here, what are highlighted in the grey color were standing outside of our shopping cart bounded context. Hence, we won’t tackle the nitty gritty details of those objects within our code implementation since those things would be in other services (e.g. product catalogue service and user service). Nevertheless, we’ll still be having those objects within our code, in the context of what our shopping cart needs. For instance, we won’t need user credentials information in our shopping cart since that should be catered from the user service context. In essence, we merely need user information which is related to the shopping cart itself, such as: user_session_id, shipping address, billing address (furthermore, address information should also be catered from another service, e.g. : address service even this can be optional).

Another thing, i won’t be exactly using the onion architecture here. I’ll be using the clean code architecture instead as promoted by Robert C.Martin a.k.a. Uncle Bob. IMHO both are quite similar.., they just have different terms to explain the structure of their layers. Nevertheless, i often use all terms from those two architecture interchangeably inside the code. So, don’t get confuse with it.., just trying to focus with the layered architecture and also couple things from these following rules:

  • Dependency between layer should be going inward in one direction. As far as i concern, it was designed like this to avoid any circular dependencies.
  • A layer should depend only on one direct layer (one level) beneath it, it shouldn’t know any layers beyond that level. For instance, Aon B ( A →B ), and B depends on C( B →C ), yet A shouldn’t know anything about C.
  • Each layer should depends upon abstraction ( interface ) rather than implementation. This complies with what Uncle Bob said about 2 rules to fix RFI issues, you can read it from my old blog here.

Let’s start by looking on some DDD basic building blocks for our shopping cart as depicted above.

According to what i wrote here, there are some technical building blocks within DDD, i.e. Entity, Value Object, Aggregate, Repository, and so forth. You can visit that link again to clarify the differences between all those things. Not all of those things are needed when we’re starting to implement our code, so just stick what we need based on the case (YAGNI).

First thing first, let’s create our project folder from the terminal and also initialize the go module as shown below:

Initialize Project

Next step is by creating our pkg directory (mkdir pkg) from the same terminal. This pkg directory will contain our implementation logic for the shopping cart which complies with the clean code architecture we’re discussing here.

So here’s our project structure looks like for now:

Initial Project Structure

In terms of the clean code architecture, by referring to the depicted shopping cart process diagram, we’ll be having these following entities within our code:

  • User (this also represents a Buyer in theshopping cart bounded context, i.e. not all information need to be revealed here)
  • Cart
  • Product (used as a placeholder for the product information. And for the sake of simplicity…, we won’t have any product categories because our goal here merely to discuss DDD with Clean Code Architecture).

So, i create those 3entities within my pkg/domain/entity folder. To keep it simple, i have marked all of the fields as exported fields (later on, we can think about hiding the fields information behind the setter / getter which commons in Java).

Cart Entity
User Entity
Product Entity

And our value objects ( pkg/domain/valueobject ):

  • Cart Items (strongly related to product entity which the details information should be fetched from the product catalogue service, and this is beyond of what we’re discussing here).
  • Buyer Address (comprises of Billing and Shipping addresses, determined by the Address Type)
CartItem Value Object
BuyerAddress Value Object

And our enum objects under sharedkernel/enum directory as following:

CartStatus Enum
AddressType Enum

If you’re wondering about what shared kernel is, you can read it from my first part here.

The next important thing to do is to implement our core business logic (a.k.a. core domain in Onion Architecture). This is something that is related to the enterprise business rules out of the application service rule.

In order to implement this kinda thing, we need to setup our aggregate root (i call it as user_cart aggregate). Again, take a look at what i wrote here if you’re still wondering what is the aggregate all about.

User Cart Aggregate Root

Another to-do thing is to setup our repositories. Bear in mind, that when we’re talking about repository, it’s not always about the database. The storage implementation could be anything such as: web services, file, in-memory, etc.., and they should be abstracted by following some contracts defined on the interface. In that way, we can use them interchangeably later since we make our client code depends on the interface rather than directly on the implementation. In order to make it possible, we should start by abstracting our repositories with interfaces such as following:

Cart Repository Interface
User Repository Interface
Product Repository Interface

I would assume that some of you will be asking me about those empty interfaces :-). Why do they exist ? Why do we use them ?.

As far as i concern, we’ll be having different models for every layer in order to make our architecture more loosely-coupled. In that case, we should abstract our interface model as far as we can, thus we can have more flexibility within our code. Let’s say for the user repository interface, we can have 2 implementations for it, one implementation to get the user information from the database, and another one to get the information from a web service (e.g. REST). Therefore, definitely we’ll have 2 models here, one based on the database model, and another one is based on the web response model. That’s the reason why we use empty interface ( interface{}) within our repository interface. You get it right ? :-). Don’t worry, everything will become clear once we implement our interface.

For the sake of simplicity, i’m going to implement in-memory repository for now since our first goal is merely to discuss about how’s our clean code architecture will be looked like. However, i will do as I can to incrementally update the code later for another storage / repository, such as database or web service. Feel free to follow my account on GitHub for this.

Let’s start to implement our repository interface by creating another directory (outside the domaindirectory, but still inside the pkg directory) as shown below:

Setup the placeholder for In-Memory Repository Implementation

And here are our models (inside the pkg/adapter/repository/inmem/model directory) to be used in the context of our in-memory repository (remember about what i explained earlier about the interface{} ,when we designed our repository contract interface)

User Model for our In-Memory Repository Implementation
Cart Model for our In-Memory Repository Implementation
Product Model for our In-Memory Repository Implementation

followed by our in-memory repository implementation for those 3 models respectively (inside. /adapter/repository/inmem directory):

User In-Memory Repository Implementation
Cart In-Memory Repository Implementation
Product In-Memory Repository Implementation

And the use cases for the cart and user as written below :

User Port Interfaces
User Usecase Interactor
Cart Port Interfaces
Cart Usecase Interactor
Product Usecase Interactor

Last but not least, let’s create a small CLI program to test our shopping cart functionalities that we’ve created so far.

Shopping Cart CLI (using in-memory implementation)

Ok, some of you might get overwhelmed with all of the code here (while some of you might not), especially in regards with the use case port and interactor (what would be the differences between those 2 ?). Hopefully it will be cleared soon.

Meanwhile, here’s our project structure looks like so far:

Our Shopping Cart Service Project Structure with In-Memory Repository

And here’s when we’re running and testing our CLI program:

CLI Shopping Cart with In-Memory Repository

You can grab the full code here. Again, feel free to follow my GitHub account so you won’t be missing any update that I made in the code :-).

Let me know for any concerns or questions from you folks :-).
Read the next part here.

Any comments or suggests would be well appreciated.

--

--