- Notifications
You must be signed in to change notification settings - Fork49
License
ddd-by-examples/library-nestjs
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This project is an implementation of the well-knownddd-by-examples/library, but this time using TypeScript and Node.js.
This is a project of a library, driven by realbusiness requirements.We use techniques strongly connected with Domain Driven Design, Behavior-Driven Development,Event Storming, User Story Mapping.
The project is currently under development. Some solutions are temporary and may change.
A public library allows patrons to place books on hold at its various library branches.Available books can be placed on hold only by one patron at any given point in time.Books are either circulating or restricted, and can have retrieval or usage fees.A restricted book can only be held by a researcher patron. A regular patron is limitedto five holds at any given moment, while a researcher patron is allowed an unlimited numberof holds. An open-ended book hold is active until the patron checks out the book, at which time itis completed. A closed-ended book hold that is not completed within a fixed number ofdays after it was requested will expire. This check is done at the beginning of a day bytaking a look at daily sheet with expiring holds. Only a researcher patron can requestan open-ended hold duration. Any patron with more than two overdue checkouts at a librarybranch will get a rejection if trying a hold at that same library branch. A book can bechecked out for up to 60 days. Check for overdue checkouts is done by taking a look atdaily sheet with overdue checkouts. Patron interacts with his/her current holds, checkouts, etc.by taking a look at patron profile. Patron profile looks like a daily sheet, but theinformation there is limited to one patron and is not necessarily daily. Currently apatron can see current holds (not canceled nor expired) and current checkouts (including overdue).Also, he/she is able to hold a book and cancel a hold.
How actually a patron knows which books are there to lend? Library has its catalogue ofbooks where books are added together with their specific instances. A specific bookinstance of a book can be added only if there is book with matching ISBN already inthe catalogue. Book must have non-empty title and price. At the time of adding an instancewe decide whether it will be Circulating or Restricted. This enablesus to have book with same ISBN as circulated and restricted at the same time (for instance,there is a book signed by the author that we want to keep as Restricted)
The first thing we started with was domain exploration with the help of Big Picture EventStorming.The description you found in the previous chapter, landed on our virtual wall:
The EventStorming session led us to numerous discoveries, modeled with the sticky notes:
During the session we discovered following definitions:
This made us think of real life scenarios that might happen. We discovered them described with the help oftheExample mapping:
This in turn became the base for ourDesign Level sessions, where we analyzed each example:
Please follow the links below to get more details on each of the mentioned steps:
At the very beginning, not to overcomplicate the project, we decided to assign each bounded contextto a separate package, which means that the system is a modular monolith. There are no obstacles, though,to put contexts into maven modules or finally into microservices.
Bounded contexts should (amongst others) introduce autonomy in the sense of architecture. Thus, each moduleencapsulating the context has its own local architecture aligned to problem complexity.In the case of a context, where we identified true business logic (lending) we introduced a domain modelthat is a simplified (for the purpose of the project) abstraction of the reality and utilizedhexagonal architecture. In the case of a context, that during Event Storming turned out to lack any complexdomain logic, we applied CRUD-like local architecture.
We put a lot of attention to keep the consistency between the overall architecture (including diagrams)and the code structure. Having identified bounded contexts we could organize them as a set of libraries (one of the reasons why Nx is used). Thanks to this we gain the famous microservices' autonomy, while having a monolithicapplication (modular monolith). Each package has well defined public API, encapsulating all implementation details by usingnx-enforce-module-boundaries.
Just by looking at the package structure:
└── libs ├── catalogue └── lending ├── application ├── infrastructure ├── ui-rest └── domain/ ├── /book ├── /dailysheet ├── /librarybranch ├── /patron └── /patronprofileyou can see that the architecture is screaming that it has two bounded contexts:catalogueandlending. Moreover, thelending context is built around five business objects:book,dailysheet,librarybranch,patron, andpatronprofile, whilecatalogue has no sublibraries,which suggests that it might be a CRUD with no complex logic inside. Please find the architecture diagrambelow.You may ask why, unlike a Java project, all business objects don't have their own hexagonal libraries. Indeed all business objects have now only their own catalog in each tier.We changed that here because in Java these libraries made extensive use of each other, while in Node.js it will produce a circular dependency. So, when two libraries strongly depend on each other, they have to be merged together.
Yet another advantage of this approach comparing to packaging by layer for example is that in order todeliver a functionality you would usually need to do it in one package only, which is the aforementionedautonomy. This autonomy, then, could be transferred to the level of application as soon as we split ourcontext-packages into separate microservices. Following this considerations, autonomy can be given awayto a product team that can take care of the whole business area end-to-end.
NestJS is taking a big part of the market. Currently, the most popular framework is still Express, but for complex business applications, NestJS will fit better, thanks to its advanced Dependency Injection system, TypeScript as the main language, and many out-of-the-box solutions that make the development more organized and standardized from the very beginning.
In oposite to the goal from theddd-by-examples/library#spring, we will not categorically avoid dependence on our framework. As Eric Evans said in his book
The best architectural frameworks solve complex technicalproblems while allowing the domain developer to concentrate on expressing a model. But frameworks can easily get in the way, either by making too many assumptions that constrain domaindesign choices or by making the implementation so heavyweight that development slows down.
Following that sentence, we will try still using the framework in a few places to speed up the development without strongly affecting the structure of our model.
The project is still under construction, so if you like it enough to collaborate, just let usknow or simply create a Pull Request.
- Introducing EventStorming by Alberto Brandolini
- Domain Modelling Made Functional by Scott Wlaschin
- Software Architecture for Developers by Simon Brown
- Clean Architecture by Robert C. Martin
- Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans
About
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.
