This project contains some very old code that does not reflect my standards anymore. I will be leaving the project in an archived state in case it is still useful to someone.
A solution to Kata01: Supermarket Pricing.
Download the latest JAR.
The Kata's description is fairly vague giving us the freedom to define the user stories as we see fit. We assume that the supermarket has a stock of items that needs to be managed and that the user is the Stock Manager. Given that, our user stories are like so:
- As a Stock Manager user, I can add a new item to the stock.
- As a Stock Manager user, I can increase the quantity of an item so that I can update it in the arrival of more stock.
- As a Stock Manager user, I can decrease the quantity of an item so that I can update it in the sale of stock.
- As a Stock Manager user, I can see all the items in the stock.
- As a Stock Manager user, I can add a new offer strategy for the stock so that the customers can have more discounts.
- As a Stock Manager user, I can remove an offer strategy from the stock so that I can limit the customers' discounts.
- As a Stock Manager user, I can see all the current offer strategies.
- As a Stock Manager user, I can see all the applicable offers to the stock.
- As a Stock Manager user, I can see the total value of the offers that are currently applicable.
- As a Stock Manager user, I can see the total value of the stock, before the offers.
- As a Stock Manager user, I can see the total value of the stock, after the offers.
The design of the supermarket follows a 3-layer Clean Architecture, the layers are:
- Entities, which includes our business POJOs and the repository contracts (interfaces).
- Use Case, with the main business rules of our application. Each Use Case maps to a user story.
- Frameworks & Drivers, where the concrete implementation of our repositories reside.
The repositories are implemented as in-memory repositories. If we were to implement them using persistence (like a database) and we were to implement a UI (like a console application) then we could use two additional layers:
- Interface adapters, for converting the database rows to business entities and the business entities to View Models.
- Presentation, where the View Models are presented to the view. Preferably by leveraging a Design Pattern such as Model-View-Presenter (MVP).
A Package-By-Feature type of packaging is used.
The branching model used is GitFlow.
This project was made to be simple, readable and maintainable. To achieve that certain decisions have been made:
- There is no Dependency Injection framework used. If we were to create a production ready supermarket, then we would use a Dependency Injection framework such as Dagger 2. By taking advantage of such a framework we could reduce the visibility of certain internal classes (like the repository implementations) and we could also easily establish whichever resources we want as singletons.
- The supermarket does not support asynchronous operations, something that a real one should. This could be achieved by using a Reactive Extensions framework such as RxJava to manage multiple asynchronous streams. Additionally, we would have to make sure our reads and writes to the repositories are synchronized.
- All repositories are in-memory, meaning that no state is persisted. We would have to change that by making database implementations of the repositories.
The supermarket can be found in the supermarket module.
If you want to build the whole project, run all the tests and the code analysis tools then run the file:
scripts/build.sh
AutoValue provides us with an easy way to write immutable POJOs with generated equals
, hashcode
and toString
methods. It also provides a painless way to implement the Builder Pattern. These advantages allow us to have the
boilerplate code generated for us which lets us focus on the features we want to provide to the user.
The de facto framework when it comes to testing in Java. We use JUnit for both Unit and Integration testing.
This gives us a versatile and easy to use API for creating mocks, stubs and fakes for our tests. It makes the tests very readable and easy to change.
Provides a fluent API for assertions with better error messages than the default ones. The tests are more readable and easy to understand.
An extensive tester for verifying whether the equals
and hashcode
contracts in a class are met.
A tool for making sure our Java code adheres to the coding standard that has been defined. The coding standard used is the Oracle standard along with some modifications.
A static analysis tool for finding bugs in Java code by analysing the generated bytecode.
Same as FindBugs but works on the source code.
Our Continuous Integration server for making sure errors are detected before they are merged.
Instead of only having the backend exposed internally, we could expose it through a REST API.
Assuming that our supermarket's website is supermarket.com and we are in version v1.0, then the REST API could look like this:
api.supermarket.com
api.supermarket.com/1.0/stock
/items
/offerstrategies
/applicableoffers
Verb | Description |
---|---|
GET /items |
Get all items |
POST /items |
Create new item |
PUT /items/<id>/increase/<quantity> |
Increase item quantity |
PUT /items/<id>/decrease/<quantity> |
Decrease item quantity |
GET /items/valuebeforeoffers |
Get stock value before offers |
GET /items/valueafteroffers |
Get stock value after offers |
Verb | Description |
---|---|
GET /offerstrategies |
Get all offer strategies |
POST /offerstrategies |
Create new offer strategy |
DELETE /offerstrategies/<id> |
Delete offer strategy |
Verb | Description |
---|---|
GET /applicableoffers |
Get all applicable offers |
GET /applicableoffers/value |
Get applicable offers value |
This application itself is released under MIT license, see LICENSE.
All 3rd party open sourced libs distributed with this application are still under their own license.