Not very long ago I gave a talk about the clean architecture on frontend. In this post I'm outlining that talk and expanding it a bit.
I'll put li...
For further actions, you may consider blocking this person and/or reporting abuse
By the Lord's mercy, an article that isn't afraid to be big, tackle something that is REALLY important and above the "10 packages to use in your next project", and with so much useful information!!!
Alex, you're a godsend. Loved and hope to use it as material in future projects.
Small P.S.: Loved to see the Branded Types being used in the architecture. I'm also hoping to use more io-ts and more of a "functional" strategy to manage those scenarios and have more "safety".
Again great article man.
If you're already in the fp-ts ecosystem, you might consider using newtype-ts.
Even if you stick with the recommendation in the article, using the
unique symbol
approach (as in the newtype-ts examples) seems to completely avoid the string constant collision worry that ts-brand mentions repeatedly in its readme.Oh, seems interesting, thanks!
I’ll definitely check it out :–)
Definitely! I really didn't knew that you could use symbols to brand types, which is really great! I'll try to take a look at the newtype-ts. Thanks @ashoutinthevoid !
Thanks a lot! Glad you liked it!
Yeah, I wanted to gather all the experience I have with different paradigms and languages I used. And it feels like Clean Architecture with a bit of functional programming works well.
Thanks again for the comment ☺️
Thank you very much, I am just learning that now. I will have to dedicate more time to your post for a few days. Anyway I didn't know that domain logic is considered Frontend.
Thank you for the valuable information, this is exactly what I was looking for!
One of my team's OC24 LTD first projects, LegalBukmacher, was done the way we knew it at the time. It was really hard because we didn't understand how to do the architecture of the project correctly to avoid problems later. We wasted a lot of time on various edits, but now we have finally achieved the desired result. This is a really important topic for all FrontEnd developers who want to work with large projects.
Happy you found it helpful! 🙌
Great Article Alex!
I really like the fact that you explain the patterns and architecture by example and show why it's an improved way of working. The part I loved the most was: "Split Code by Features, not Layers". I believe this is the only way to avoid code duplication and bad design. I even think this is one of the strengths of using a micro frontend architecture in which the design of the application is very important.
I want to thank you for this article!
Hope to read more from you soon!
Thanks for the review! ^_^
Yeah, I wasn’t sure at first if I needed to split the code by features right at the start. I thought it could overcomplicate the mental model of “layers”.
But I felt like it was a really important part, so I decided to add a section about it after the reader is familiar with layers.
Hope it is clear enough though 😅
Hello Alex, I loved your article, and I am planning to use this architecture in huge enterprise project. I have some concerns about redux & redux-toolkit paradigm that is recommended from their documentation.
So if I understood your plan correctly in your domain you define your interface OrdersStorageService, that requires 2 things, some state, and some callback for setting the state. But then you end up with 1 reducer in redux? Because if you have single callback per model, then I assume you will end up with 1 reducer by slice, which is 'not recommended' or maybe I'm just paranoid and that's totally fine ?
I even posted full question in stackoverflow, you can find it here: stackoverflow.com/questions/716495... where I am trying to Implement your architecture with react + redux-toolkit, local state, and custom hook store.
Hey!
So, I've noticed the same question in the issue in the repository and answered it right there, if you don't mind:
This was awesome
I mean you won't get this much from an ordinary post, yeah maybe some of them would actually be understandable but this one was literally everything.
It wasn't just about clean architecture, it was a more of a "fp approach to architecture with typescript" + using good practices (like having type alias)
Most articles won't explain further or even provide an example, they'll just copy paste some high level stuff
I really appreciate your effort, and I'm waiting for future posts
Thank you! ^_^
Yup, I was trying to compile all the good practices that help me writing code in one big post.
Glad you liked it!
This is one of the most comprehensive and well-written posts I've ever read. The subject is also quite crucial. I'm not sure why the front end world (in general) doesn't seem to care about architecture or things like SoC or DI. More people will comprehend the importance and relevance of these strategies if we can share them more widely.
The main reason for using clean architecture (for example, Co2 Cartridges uk) in frontend projects, in my opinion, is to be able to swap out UI libraries with minimal effort. We lose this capability by relying on React for non-UI layers.
Thank you!
Totally. I had some hard time working with coupled “React-driven-code”, it was difficult and inefficient.
I now tend to less depend on third-party code and modules and decouple my own code from the “outside” one.
I am a chinese FE developer, this article is so goods, looking forward to more good articles !!!
Thank you!
Yeah, I’ve got some new upcoming posts planned, stay tuned! ^_^
This is one of the best and most detailed posts I've read. The subject is so important too. I can't understand how (in general) the front end world doesn't really care about architecture and things like SoC or DI. The more we can share these kinds of techniques the more people will understand their importance and relevance.
Thank you! I really appreciate you find it useful and important ^_^
legendary
Thanks! 😃
Now this is a post and should be a model of the type of stuff I really am looking for here! Post is definitely saved and I will be looking back at a lot. Thanks so much for it!
Thank you very much! I really wanted to make it as detailed and useful as I could :–)
Great article, Alex. One question. Hooks are part of React. React is about the UI layer. So why are hooks part of the application layer? It’s the same with using hooks in services. How can you reuse them across different UI libraries?
Thanks!
I described this nuance in these sections of the post:
TL:DR; it is indeed non-canonical, in a perfect world, the use case is independent from the framework.
In the sections' examples, I show how to do it using DI with the last argument for the use case. Basically, we're just passing all the things we gathered from hooks via object. If we did that, the hook would become just an adapter which is better.
However, it is not always reasonable to do. In this case, for example, hooks are used just as “DI” to inject services and work with data storage.
Also, notice that even using hooks as DI, we don't mix up the use case function and the hook that is exposing the use case to the UI. The use case is still independent from the framework.
Conceptually the hook is already just an adapter and not a part of the application layer. It's just located there because of my laziness :–)
I think that the main reason for using clean architecture in frontend projects is to be able to replace a UI library with a different one with minimal effort. By making non-UI layers dependent on React, we lose this capability.
Depending on the UI library in question, you will probably never be able to just "swap it out", and in most environments, the dev shop will have created a design system, an accepted UI suite to use (material, kendo, etc) and will hardly ever change. The real flexibility is in portability and extendability, which is more-so for your own libraries you write, not the application itself. The application is the implementation detail, and your libraries are where the clean architecture matters the most.
So long as you have decent organization and have well-built and taken-care-of library architecture, your application will probably just use the library and mock/override what is needed.
This is why Angular uses modules and allows you to inject provider overrides. React is more open-book, which is where a hook-driven DI makes sense. React Context and several other state management tools also aide in dependency injection and overrides, it's just a matter of what you prefer.
I use TypeScript + MobX and can change React to Vue or Angular at any time. So it depends on the architecture of your application.
I commented on my appreciation of this post when I first came across it but I just came back to add a thought I had:
I often see people still writing these services and repositories i.e.
NotificationService
as a class with a tonne of methods on it. You don't need classes to do this sort of thing. There's no reason why you can't just have individual functions as mini services. If you're using standalone functions instead of classes with dozens of methods, you really cut down on implementation creep and having to mock random unrelated dependencies when you're just trying to test a single method.I think this whole
services as classes
thing is just a "well we did it in OOP" thing. Start thinking in functions!Maybe we stick to this pattern because constructor injection is the most common way to implement DI but it's not the only way - and in a predominantly functional application, it's probably not even the best/easiest way.
That’s correct!
In this post, I tried to bind “clean architecture principles” and “functional core in imperative shell” with pure transformations because I think it takes the best from different paradigms.
I use objects for services here just because it’s easier for me to think of a service as a “package” with a couple of functions inside, and easier to check if the service implements the interface. (I try not to use classes if there is no need to keep some state in the entity.) So, basically, it is just a function, but in a box 😃
I also think that DI via constructors is a convenient way to compose objects, although for composing functions there are other technics like boxes and mapping :–)
In general, I try not to be an advocate for a single particular programming paradigm. For example, immutability, pure functions and functional composition are ideal for describing data transformations. But for working with state I prefer objects instead of monadic state changes from pure FP 😅
this functional approach of clean architecture will help me a lot in my next projects. I was so disappointed to only see overingeneered OO implementations.
Thanks a lot
Glad it helped! ^_^
This is the most easy to understand software architecture design article I have seen, opened my awareness, thank you ~
Thanks!
So glad it helped! ^_^
Great article, thank you for writing it @bespoyasov .
Thank you! ^_^
Great Article Alex!
I have one question.
I believe that Clean Architecture has the idea of "designing so that the outer layers depend on the inner layers".
When I look at the source code, I see that the Application Business Rules depend on the Interfase Adapters.
Thank you! ^_^
Interfaces (aka ports) are the part of the application layer. But their implementation is the part of adapters layer.
The application layer dictates the requirements for the third-party services. Since the ports are behavior contracts they are also determined by the application layer.
There is a post I refer to in the text:
…It explains this specificity in more detail :–)
Thanks for answering!
I'll read the article too!
This is a great read and incredibly thorough! Thank you so much for this. I have been following the CA approach in React for awhile but am newer to TypeScript. The way this code is seperated and adheres to the principles of the architecture while also paying mind to expediency and complexities of the code, is wonderful. I enjoy how it also leverages the full capabilities of Types, React and hooks. The only thing I'd add is perhaps a testing approach ~ clearly you alluded to it but I'm curious myself where the majority of tests we want to write would live with highest levels of confidence. Also how would you handle complex derived presentational logic, would it be seperated as well and then wrapped in useMemo? Once again great article.
Thank you very much for the review! Really glad you find it useful 😊
Yup, the tests are a very important tool for developing robust software. I intentionally skipped tests in this post to avoid distraction from the main topic. Although, I mentioned the testability and how to improve it in the post text. So basically, we keep tests in mind along the way :–)
As for the complex UI logic, I would add some presenters to handle that. In my opinion, it’s better to separate the UI logic from the application logic. So basically, application layer would reflect the app data transformations, and UI presenters — only the complex UI changes.
In some cases, presenters might resemble finite state machines. I find them a very convenient way to describe the UI changes.
Amazing article, thank you! Really clear explanation with loads of examples. 💪
I'm just wondering why you don't use the concept of Presenters of the Clean Architecture? Is there any drawback in using Presenters?
Thanks! ☺️
About presenters, no drawback, it’s just the app is so small that there wasn’t any need for additional data transformations before render ¯_(ツ)_/¯
In the real apps, there might be a case for them, depends on the complexity of a system.
In my experience though, the only mediators between a use case and the UI were formatters and data converters for components. Basically, my teams and I managed to split all the processes is such a way and create such DTOs that the UI logic became insignificant and mostly was about rendering stuff and handling user events.
Jeeze, this has got to be one of the best produced posts on Dev.to.
I learned a lot and probably still need to reread it a couple more times to fully get some of it. The list of sources at the end is something that needs to be done far more often.
Thanks! Glad you liked it 😊
This is by far one of the excellent pieces I have read on the Dev.to . Thanks for pouring it out for us. This will go a long way to help me in frontend engineering.
Thank you! I'm glad you find this useful ^_^
I'm not even a frontend, but I still appreciate the post. Thank you!
Woah, that’s so cool! Thank you ^_^
Hey @bespoyasov
First of all Thanks! You Are Awesome!!
i think there is some dependency issue:
look here:
github.com/bespoyasov/frontend-cle...
this is the OrderProduct in application layer, and if you see the import statments you will see that he load directly stuff from Adapter which is on Other layers.
in this case for example: "import { useNotifier } from "../services/notificationAdapter";
"
This is break the Dependency rule... (it wrote all over the code BTW)
WDTY?
Thanks
Hey!
Thanks! :–)
I believe, I answered a similar question some time ago in the issues on GitHub:
In short, yes, the app core should not import the adapter implementation, but in this example, it is a “DI-mechanism” that imports the adapter implementation. The use case function itself only depends on the type of the service and can be extracted into an independent module so that the hook becomes just a meaning of “glue everything together” and inject dependencies.
For more details, please, check out this issue:
By the way, I'm now working on a new post series about this very topic! :–)
Stay tuned for more info!
This is seriously great and exactly what I was looking for. Seeing how you do this with TS interfaces and modules as opposed to OOP really helps me to understand it as a frontend guy. I'm trying to level-up on how I architect my features and this is a great starting point. Thank you.
I’m so glad it helped! ^_^
Awesome article! Respect the effort you put to create this article 👍🏻 Thank you for sharing, love it 💯
Thanks! 😊
I wish CSS/SCSS would be mentioned every time we talk about frontend...
With this approach any modularized CSS approach should work fine!
What do you mean by modularized? How does it play together with the cascade? Can you define it for me? I don't know if I can agree or not yet :).
Wow, a high quality content.
Makes you look at the front-end development from a whole new angle.
Thank you!
Thanks! 😊
I was always frustrated by the lack of 'sane structure' that is independent of libraries/ frameworks in the front end development as I was used to in backend side. Now I saw Clean Architecture can be applied here too! I think I am going to like working in frontend projects. Thanks!
Thank you!
Glad it helped ^_^
Great Article! Thank you for your effort!
Just being curious, have you applied clean architecture at work, or are there any open source project which introduced this practice.
We are considering to apply clean architecture in our production, while also concerning the cost and lack of real world examples.
And also personally I am wondering why clean architecture is not popular in the frontend world, is the cost even higher so that most of time we just give up on those benefits of clean? Or are there any other architectures to achieve those best practices which we could gain from clean.
Thank you!
Yup, I apply this approach in my work. A specific implementation might differ but the overall idea stays the same.
Most of my projects' repos are closed though, but I've got one open-source project where I use this approach: github.com/bespoyasov/tmstmp.
IMO, this architecture style is suitable for applications with rich domain logic which isn't always the case with the frontend.
Sometimes, frontend applications are “thin client apps” and the main logic is kept on the server. In these cases, it's not really necessary to develop sophisticated architecture for the client app.
Also, on the frontend, there's huge impact of reactivity and FSM programming. The nature of UI imposes a lot of constraints on the code style and data flow for the frontend development.
Making UI deterministic and declarative is difficult, and sometimes clean architecture just doesn't fit the requirements.
Hi guys!
Fantastic deep dive into DDD and clean architecture here! I've been building production apps using these concepts and I can attest it pays off.
I've put together a simple React application that embodies the principles and strategies discussed here, showcasing various state management approaches (React state vs Redux) among other patterns, to illuminate the real-world flexibility and scalability these architectures afford. It's a really simple app but you can use it as a guideline for your projects!
github.com/nicmesan2/todo-list-cle...
Of course, any feedback is more than welcome :)
Cheers!
You are the best dude, Imma share this post all around. Thank you <3
Thanks a lot! Appreciate it ☺️
Awesome article @bespoyasov! This approach seems the most effective one I’ve found about clean architecture. Have you tried using react-query in any of your projects? Although it helps you with cache and loading state, errors, refetch, I think it would inject too many logic in the usecase. I’ve seen in your example you put the loading state in the component (I suppose to make the business logic cleaner). Therefore, how react-query would fit inside this architecture? Would it be better to use it in the component surrounding the usecases or would you directly use it inside the usecases?
Hey! Thanks!
Usually, I abstract such functionality in a “service” so that it doesn't mix up with the business logic in the use case.
IMO, the use case functions shouldn't be “complex”, it should be an “orchestrator” that composes various services and domain functions into a pipeline.
In my projects for client fetching and mutations, I prefer
useSWR
but I suppose react-query would fit the same way.The logic of caching, deduping, and managing loading state could be a function or a custom hook that exposes constant contract for the application layer. The app layer would use this hook as another “service” (either application service, or an “adapter” to external service).
There's a question about if it's worth it to completely decouple the SWR or react-query from the code of this hook but that would depend on particular goals we want to achieve.
Can the domain layer call the adapters directly? or only the application layer can interact with the adapters? thanks!
As I mentioned in the post, the canonical way assumes distinction between the functionality.
That means, there not only should always be the application layer, but also layers should have their own DTOs.
However, not every project needs all this. In the post, I mentioned a couple of reasons for not using all of the “canonical stuff”.
well , if domain have DTO, it then has side effects, no good for tests I think, Is there a principle like "keep domain layers pure, easy to test" ?
DTO (data transfer object) is just a data structure, it doesn’t affect “purity”.
Layers should be decoupled, so that changes in one of them don’t affect others. DTOs are one of the ways to do that.
In the domain layer, DTOs can be the domain entities themselves. In other—specific objects designed in a way to provide only as much information as needed.
(You can find out more about this in, for example, “Domain Modeling Made Functional” and “Domain Driven Design”.)
But again, not every project needs this. Smaller apps can live without that strict separation. Every solution depends on the particular problem 😃
Thanks for sharing this high quality article.
What do you think about transforming currentDatetime into lib / datetime.ts as a service that implements a port interface?
Thanks! Glad you liked it ^_^
I would use that if the domain entity is strictly pure but we have a requirement because of which we need to somehow use a third-party service for dates.
In this case, the entity would take the datetime as an argument. The use case function would call the service, receive the datetime and use it to create the entity.
And it still would fit in the impureim sandwich approach:
But in simpler cases I would go without extra services if they are just “language feature wrappers” 😃
nice article! I have questions:
With Java style OOP there's a lot of ceremony involved in making classes composable compared to pure functions, and if you're changing a class it's harder to tell whether it will break dependencies and composability while changing a pure function is in principle fine as long as the parameter and return contracts stay the same.
For building UI and application flow it helps a lot to have a set of components that are easy to judge whether they compose well and to what extent they're likely to cause problems when modified. Certain OOP implementations overcome this, you could check out the GUI library and contracts in Racket, Common Lisp CLOS or perhaps Pharo Smalltalk.
so, class itself is a bad design? the new comers of programming langue(rust,t go) just remove it.
Depends on the purpose and who's implementing something. In single threaded applications mutable data structures implemented as classes might be a good option for performance reasons.
Golang has structs, they're kinda-sorta object-like, but no classes. Rust is designed to be a good fit for C++ developers and sticks to inherited designs except when it conflicts with the borrower and type system.
Thanks!
1) These principles are applicable to any paradigm. There are lots of books about using the clean architecture with OOP, but very few resources about that with any other. Also, fronted is more close to multi-paradigm and FP than to OOP.
However, I wrote about OOP as well, take a look 😃
2) No need for them in this particular example. They would complicate the post so it would become more difficult for people who aren't familiar with the concept.
I wanted to keep the post as close to the real world as possible but as simple as possible at the same time.
Thank you Alex
Really high quality article.
Thank you!
Thanks for reading! ^_^
Very good article!
modules and dependency injection peculiarity relies on Typescript and Rxjs, Angular is a good fit for this architecture.
Thanks!
Yeah, I guess so :–)
Quite detailed. Thanks for sharing
Thank you :–)
Thank you very very very much
Appreciate it! :–)
Loved the article, thank you. I'll examine it more over the years to come as "architecture", particularly in front end, is one of my sticking points.
Thanks! Glad you liked it 😊
this is awesome!
Thank you! ☺️
Amazing article! We need more of this! Well done Alex 👏
Thank you very much! 😊
Thank you very much for this great article! I just translated the comments and texts in your repo. The translated stuff can be found here:
github.com/shorthander/frontend-cl...
Awesome!
The README file I have translated before, but I totally forgot about the comments in the code 😅
If you want, you can make a PR, I will merge translated comments in the main repo :–)
Done :)
Merged, thanks! 🥳
Love it! Thank you!
Thanks!
Glad you liked it ^_^
Wow nice work, thanks for sharing!
Thanks! ☺️
Nice work, appreciate it!
Thanks! ☺️
Clean Architecture on the backend (reference implementation): domain-driven-hexagon
Thank you for the great article. By the way, how to deal with side effects like error handling or useEffect from state changed saved in redux in terms of clean architecture?
Hey! Thanks for writing, glad it was useful!
I believe I answered a similar question in some of the issues in the repository of the code samples:
Hope it helps! 🙌
Appreciate it ☺️
Brilliant post, excellent work!
Thank a lot! Glad you liked it 🥳
Excellent article!
Thank you! ^_^
One of my best article about hexagonal architecture on frontend 👏
Thank you so much! ☺️
thank yoo ,
where the valation of the fields should go
Depends on the implementation. As for me, for the UI validation, I usually create a validation “service” that is used in the application layer.
Although, if we use canonical OOP we can place validation in the entities' constructors to avoid creating invalid entities in the first place.
Thank you for the great article. By the way, how to deal with side effects lik error handling or useEffect from state changed saved in redux in terms of clean architecture?
about adapter, I think event system(like reudx) is more decoupled and simple than it.
the application layer just receive and dispatch events, don't care anything else .
Adapters are needed to connect 3rd-party services to the app in a decoupled way.
Redux solves the problem with decoupling components, but it won’t help to connect server fetching API or reading from
localStorage
.In fact, it even needs its own adapter to avoid using Redux’s injector-hooks everywhere in the app since it itself is a 3rd-party module.
as a principle of DDD, only the domain layer can have state, am I wrong? your domain layer are just pure functions, so how did you think about this ?
Not necessarily.
The “Domain Modeling Made Functional” book can be a good example of functional approach to DDD.
why is your domain Order is not a class ? there is a reason behind that or just a func vs oo programming preference ?
Good question.
The post was based on a talk about the clean architecture for react-developers.
My point there was to show that these principles are applicable to any programming paradigm, not only OOP.
As for OOP, I have series about using in on the frontend. You can check it out here :–)
lgtm