bespoyasov / frontend-clean-architecture

React + TypeScript app built using the clean architecture principles in a more functional way.
https://bespoyasov.me/blog/clean-architecture-on-frontend/
2.35k stars 258 forks source link

How to define Domain in practice? #11

Closed zybzzc closed 1 year ago

zybzzc commented 1 year ago

Close to the database table (API) design? Close to the UI model design? Or should it be designed close to the business alone?

If it is close to the back-end API, it may be inconsistent with the data model needed for most front-end scenarios.

If it is close to the front-end UI model, it will become inconsistent when requirements change or when there are multiple clients.

If you design it completely by yourself according to the business requirements, first of all, the design cost and difficulty will go up, and there may be a small amount of the above two problems, i.e., it needs to be adapted to both the back-end API and the front-end UI model.

bespoyasov commented 1 year ago

Hi!

How to define Domain in practice?

The short answer is “it depends”.

Close to the database table (API) design? Close to the UI model design? Or should it be designed close to the business alone?

I've seen each of these in projects I worked on, and each has advantages and problems. There's no silver bullet for all situations, so for every project, we'd need to weigh the benefits and costs of the solutions.

In the second part of the question, you correctly pointed out the potential problems of the given options. To decide between those, we'd first need to assess the resources we have, the goal and the lifespan of the project, and compromises we can agree on.

Basically, we need to figure out critical and non-critical aspects of the frontend app we're going to build. What problems we can tolerate and what problems aren't desirable.

It's difficult to speculate on such a topic abstractly because without specifics of a project it's almost guaranteed to assume wrong. Because sometimes we don't need any “architecture” on the frontend at all and the assumptions we're going to make would be incorrect. (Like, for example, in the case of the thin client where all the business logic is located on the server.)

I know it's a “boring answer” but without specifics, it's the only answer I can give 😃

zybzzc commented 1 year ago

Thank you! Sorry for responding so late.

I actually realized shortly after I asked this question that it was a "case-by-case" issue.

Right now, I'm trying to build a ticketing site for multiple ends and types of users using a clean architecture. I think I should choose to follow the API to model my domian because it is unified.

But I still have some problems.

  1. how do I implement my store adapter? Do I need to put this part of the adapter with the framework dependency into a different client project or choose a framework agnostic three-party state management library to do it.

  2. do I need to fully use the state manager to host all the state in the application that contains UI state? Then each end is just some pure function component. But I think this will accelerate the corruption of the core business layer.

bespoyasov commented 1 year ago

I can't consult you since I don't know the specifics of the project but I can try and explain the thought process I'd apply to solve these questions :–)

how do I implement my store adapter? Do I need to put this part of the adapter with the framework dependency into a different client project or choose a framework agnostic three-party state management library to do it.

If I were to decide, I'd first consider external constraints that could drive the project:

Let's break down all these.

What's the scope of the feature? Can it be isolated? The point of any architecture is to make the project long-lived, extendable, flexible and less-constrained. So if the feature I'm going to build is an isolated module (cough... “microservice”) that is developed, built, and delivered separately (e.g., custom analytics) then it might be a good idea to not depend on the specific project stack.

If not, it might not worth it because we could spend a lot of resources on an adapter that wouldn't actually do its work. Abstraction only in the sake of abstraction is usually overhead. If we know that we will integrate the feature into an existing project that isn't likely to change there's no point in making it absolutely independent.

Although, we might declare an interface (or type) to specify the boundary of this feature so it's clear where it ends. This boundary might not really “adapt” anything per se but it can be a constant reminder that here we might decouple the system further if we need to.

How many people would participate in the development? Is it the team of 10 people? Is it only 2 developers? Is there a process of enforcing architectural constraints? Are those constraints and enforcement healthy? How will it affect the development?

If the project is an experiment where only 2-3 people develop the app and they want to try something new—then no problem, but we need to understand that the goal here is mainly to experiment.

How long the feature is going to live? Is it likely that the project is going to live long enough to meet difficulties with changing tooling? Have we already experienced such thing earlier?

If we know that for some reason the project will change the infrastructure (store, API client, network transport) or we'll need to extend to be using native APIs (pure web → native web view) then it might be a good idea to abstract the interface and specify the boundary.

Again, we say “it might be a good idea” because we don't know for sure and we don't want to prematurely overcomplicate the project.

What's the goal with creating such an adapter? Do we want to conduct an isolated experiment to see how this architectural style fits the project or how it helps to solve an existing problem? We're good to go!

Do we want to do this just because “it's right”? This reason might not be enough because, first, it might consume unadequate amount of effort, and second, it could make the project overcomplicated.

Abstracting infrastructure is a tool that solves particular problems so we should consider using it when we have these problems and not just “doing architecture”. Poor decisions might make the project too complicated and overdo the processes of development. This could scare the developers, make the development slower, and become a reason for people to leave the project.

Something else specific to the project that I'm not aware of? Are there any hidden unknowns? Am I sure the domain model is complete? Do we have the domain at all or is it just a CRUD UI with a limited set of screens? What are the plans on evolving the project? What does PM or PO say about the plans on the project?

Generally it's a good idea to talk to different people from the product and collect their thoughts about it even after we think we've gathered all the requirements before hand, just to see if we overlook anything. It's also worth it to make regular “health checks” and see if our understanding has started to differ.

After we've answered these (and some other) questions, we might have the rough idea of how and why we need (or don't) to abstract and implement the store for the project.

do I need to fully use the state manager to host all the state in the application that contains UI state? Then each end is just some pure function component. But I think this will accelerate the corruption of the core business layer.

I'd consider this an implementation detail and it would depend on the need you have. Usually, I try to decouple the logic data and the UI data, or at least understand when I merge them (where, why, and how to deal with it later if needed).

It doesn't necessarily mean that I don't ever put the UI data into the store or, on the contrary, don't use store for the logic data. But I try to leave some ways to “change the way we do stuff” so that it'd be a) possible to change the strategy and b) wouldn't consume a lot of resources.

Hope this helps! 👋

zybzzc commented 1 year ago

Thanks a lot!

After discussions with my other development and PM colleagues, we now decided to model and implement the business logic as well as the UI component design for the back-end API, since the core ticketing logic and API are consistently the same for all clients.

In addition, we plan to unify the technology stack with the previous related projects, and then use monorepo to manage the above core business logic modules and all client modules in a unified way. This way state management, tool libraries, etc. do not need additional abstract interfaces and then adapt, directly using the same set of framework best practices simple and clear.

Although I personally would like to try a strict but complex layered architecture design, but for the current project is really not a good choice 😂

Finally, thank you again for such a careful answer!