Due to the scale needed an e-commerce system, usually relies, on multiple distributed systems each one involving many components.
This means that the design and implementation of even only one element has an high level of complexity also to support the versatility required by the business domain.
It is pretty common to use Domain-Driven Design to analyze an area of the business, a DDD subdomain, maybe with some EventStorming sessions, with the goal of getting a better awareness of concepts, processes and rules that belong to it and start talking all the same ubiquitous language.
Multiple subdomains cooperate by means of integration events and web APIs and even within a single subdomain it is possible to find one or more microservices that, whenever possible, use a choreography rather than orchestration integration model.
The implementation of a single microservice is often based on the ports and adapters pattern also known as hexagonal architecture.
Moreover, when different use cases need different levels of scale and flexibility, the CQRS pattern is used, resulting in different write and read models often needing polyglot persistence, sometimes in combination with Event Sourcing.
This is what goes under the term Event-Driven Architecture.
The goal is usually taming complexity by decomposing a huge system in multiple simpler parts with only one responsibility. Unfortunately this brings along a big increase in the overall system complexity and the challenges related to its implementation, deploy (e.g. SDLC, release management, containerization and container orchestrators like Kubernetes), handling (observability), maintenance, evolution.
One of the key element for any retailer is the shopping cart, essential both for customers to place order and for the company to make profit.
Considering the small number of articles in the catalog, the not so complex promotion rules and the quite simple cart features to be supported, I believe that the solution should be as simple as possible.
It would be overkill to use DDD, CQRS, ES, a full-fledged hexagonal architecture and code with full instrumentation for observability (logging, application and business metrics monitoring, distributed tracing at least).
The client application should communicate with an API, acting in the same way as usually a backend server does behind a frontend component, be it a Backend For Frontend or the backend part of a microfrontend.
A cart microservice exposing the functionalities required through a web API without authentication or authorization is the right choice in this case, but there would be no reason to deviate from the goal and also implement separate microservices for the catalog and promotion features, especially considering that it would require a huge effort to mimic only a little part of the features of a modern persistence store and that actually catalog and promotion are two other subdomains.
Tests should be used to guarantee that main use cases are covered and the software is maintainable and evolvable without introducing misbehaviors or regressions.
As per the build and test phase few or no scripts should be used and it would be premature to introduce Kubernetes and Helm at this stage.
The PoC contains a possible Go implementation of the solution developed using Visual Studio Code:
- a command line client
- a web API leveraging on the Gorilla mux package
Code has been tested for the main use cases and behaviors, but no benchmark or test coverage has been used.
The build and test phase supports Docker and Docker Compose
- Level 2 of the Richardson maturity model
- Media type
application/json
: no hypermedia controls even if links are embedded in responses and location headers are used - Support for conditional requests:
ETag
,If-None-Match
,If-Match
headers - In-memory storage, implemented with simple data structures, to handle articles, carts (and their ETags) and promotion rules
- Add article with quantity (
POST
) and set article quantity (PUT
) routes to implement the desired add article capability - Catalog route only to support client (improperly put in the cart service to avoid creating an API only for it)
- The promotion engine, based on rules related to an item or the cart, determine percentage/value discounts or new values that:
- are applied to part of the quantity of a cart item
- are applied to the cart subtotal
- determine a present with a quantity to add to the cart (usually a gift or a sample)
- are applied to shipping costs
- Install Go SDK
- Execute the following commands from the terminal:
cd $GOPATH cd src git clone http://github.com/luigiberrettini/shopping-cart-kata cd shopping-cart-kata go get -d -v ./...
- Run tests by executing the following command from the terminal (
-count=1
prevents caching):go test ./... -timeout=60s -parallel=4 -count=1
- Run the applications by executing the following commands from the terminal (check that the
bin
subdirectory ofGOPATH
is in thePATH
):# First terminal go install -v ./... cartsvc cartcli
# Second terminal cartcli
- Install Docker
- Execute the following commands
git clone http://github.com/luigiberrettini/shopping-cart-kata cd shopping-cart-kata docker build . -t cartools docker network create -d bridge bridgenet docker run --name cartsvc --hostname cartsvc --net bridgenet -p 8000:8000 -d --rm cartools /cartsvc -listen=cartsvc:8000 -authority=cartsvc:8000 docker run --name cartcli --hostname cartcli --net bridgenet -ti --rm cartools /cartcli -baseUrl=http://cartsvc:8000 docker network rm bridgenet
- Install Docker
- Install Docker compose
- Execute the following commands
git clone http://github.com/luigiberrettini/shopping-cart-kata cd shopping-cart-kata docker-compose build docker-compose run cartcli docker-compose down
- Install VirtualBox and its Extension Pack
- Install Vagrant
- Download the Vagrantfile provided with this repo
- Execute the command
vagrant up
- Connect via SSH to localhost on port 2200 with username
vagrant
and passwordvagrant
- Execute step 3 of the previous section
- Use a static analysis tool to keep code quality high
- Use code coverage tool
- Add a few integration tests (evaluate use of Docker)
- Instrument the application for distributed tracing
- Track and publish application metrics (add also benchmarks) and business KPIs
- Add logging in a way that it is possible to easily switch the logging target (e.g. terminal, file, DB)
- Add authentication and authorization to convert anonymous carts into the user cart
- Use Kubernetes and Helm to support advanced deployment and scalability scenarios
- Use DDD, CQRS, hexagonal architecture, domain and integration events and evaluate using ES
- Support article removal explicitly and by setting the quantity to zero
- Extend support for conditional HTTP requests:
- strong ETag validation
Last Modified
If-Modified-Since
If-Unmodified-Since
If-Range
- Move to a higher Richardson Maturity Model and Amundsen Maturity Model also using a proper API design methodology and a high H factor media type (comparison chart) like Mason, Hyper or UBER
- Implement catalog service and subdomain (evaluate using GraphQL)
- Implement promotion service and subdomain (evaluate using GraphQL)
- Use a distributed cache for carts persisting logged user carts also on a NoSQL store
- Use a Lucene-based store to search the catalog
- Use a promotion engine relying on a rule engine to back the promotion service handling complex business rules support