Opinionated example of how you can apply SOLID principles and CA to UI Web development. Based on Uncle Bob Martin Clean Architecture and Functional Design books.
You will see UseCases, Entities, Presenters, Controllers, inverted dependencies etc.
And TESTS of course - simple, fast Developer tests (as Kent Beck commanded).
(experiment: copy pasted tests just to make plenty of them and ran. result - 20k tests passed in 4s. try to beat it)
Used mocha/chai for testing (you prefer jest - you are my enemy).
The examples are not intended to be "true" FP, since true FP is readable only to chosen ones.
The examples are not intended to be "true" OOP. Because... because!
The intent is make it conceivable to everyone.
Caution
VERY DRAMATICALLY SUPER IMPORTANT NOTE:
If you don't like something - I don't care...
If you have better ideas (unlikely:) - create a PR and prepare to fight.
The project contains core module and it's integration to the different ui frameworks, like react, angular, vue etc. There is also pure native example.
It showcases how you can split business logic not only from view, but also from framework(!) and even platform (yes there is react-native integration).
- Usecases and Entities can be found in core (order, order-list).
- Simple inverted dependency examples are in core/src/dummy-dependencies.
- More complex presenter example is here.
- Since the system is very simple, controllers are quite degenerate and so not extracted, but there is one example here.
- Usecases and entities are written in procedural style. On UpdateOrderList usecase example you can see alternative FP or OOP style. Style doesn't really matter.
Regarding tests.
Such architectural approach allows you to implement classical testing pyramid -
many unit tests, some integration, few e2e.
Whole application logic is tested with fast and simple unit tests, like here.
Integration tests should test only... integrations, not application logic!
These are things like custom hooks, base classes, inverted dependencies etc.
Note
For holding state the Atom is used.
It really doesn't matter what you are using under the hood.
You just need some util to rerender view when presentation changes by a UseCase.
It can be existing tool or you can implement yours, the bestest and perfectest one.
Note
UI is primitive, because I'm not going to waste time on styles; only structure and use cases matter.
But your are welcome to PR better styles.
To run example locally, clone repo, then navigate to example you are interested in (i.e. packages/react-example). Then:
npm i
npm start
See instructions in terminal.
Note that all arrows cross the App boundary inwards. This means everything is dependent on business logic.
This is acheived by Dependency Inversion.
This allows easy testing, since any inverted dependency can be substituted by a test double.
Also there is no need to render markup and simulate events for executing a UseCase. It can be simply called from the test suite.
- [abstractionName]? means abstraction can be omitted for some reason. e.g. controller can be so simple and degenerate that it would be unreasonable to abstract it from a framework.
- "framework controller" means a file where you usually bind view to the data and handlers.
e.g.
.jsxfile where you write functional component for React or[name].component.tsfile for Angular. In this project for consistency between frameworks those files are placed to the[component-name]folder and calledindex.[js|ts|jsx|vue]
In this example I implement part of backoffice for the online store. I do not intend to cover all edge cases and implement fully functional online store admin app. I intend to implement practical example of several parts just as a showcase. The following user stories will give you an idea of what is covered.
User should see list of the orders (first page) and total order count.
Each order should contain id, created date, customer name, sum, payment status, fulfillment status.
While loading the orders, user should see some indication of that.
User should be able to open the order to see its details.
User should be able to remove order from the list.
Removal should be confirmed by user.
While removing, user should not be able to interact with the order.
User should be able to load next batch of the orders
(It might be infinite scroll or pagination, doesn't really matter)
User should be able to refresh order list in order to see current order data
User see all order details.
User should be able to change allowed fields.
For now those are: payment status, fulfillments status, shipping address.
User should be able to save changed order.
User should be able to close order.
Closing should be confirmed by user.
