What are the Domain Objects in React/Nextjs? It's an abstraction that we need. In a nutshell, these are objects that represent our business. They come from your database/API. Examples: Key domain objects in banking: (things my team deals with at Erste Digital) ↳ BankAccount, Payment, Transaction, Credit Card, Customer. In media streaming (e.g., Netflix): ↳ Movie, Series, Episode, Subscription, Consumer. In hospitality: ↳ Booking, Hotel, Room, KeyCard, Guest. It's a good idea to always have Domain objects and operations on them abstracted away from the rest of the code. Because these will be highly reusable: (across the React/Nextjs codebase) - Hotels are within the accommodation search feature - But a single hotel is also in the guest trips feature - And in the guest reminder/notifications feature Here's what such a Service has: ✓ It contains the shape of the Domain object ✓ Provides operations on the Domain object ✓ Resolves Domain object business logic ✓ Fosters reuse across the codebase ✓ 100% covered using unit tests ✓ Retains high cohesion Business requirements change. React components change. Styling changes. Your Domain stays the same. Have it separated. // TODO: Repost to your network ♻️ P.S. What are the key domain objects in your project/business at work?
📌 Domain vs Application logic (in React app) What's the difference? The main differentiator is what the logic operates on: 1. If it's a Domain object (e.g., that comes from the API/Backend), this is domain logic. 2. Otherwise, when logic is involved in operating on something that is our React app-specific (e.g., view model factory, form validations) — it's application logic. Understanding this helps to split the Service functional layer (a term from Clean Architecture), which enables high cohesion and reusability.
this architecture looks clean i like it. but talking about clean code i would: 1: transform the functions in constants receiving arrow functions 2: export the constants all at once in the end of the file as a object 3: remove return statement on arrow functions they aren't necessary 4: add ENUM or constants for the hardcoded strings 5: remove simple comparison such as booking.status == "confirmed" and use the enum instead on the code implementation (because this can grow a lot and would only increase the size of this file) if future changes kicks in they can be done using the enum and if you see how you used status == refundable this way but confirmed other way you will understand why i avoid abstracting for those simple checks. and lastly maybe using interface instead of type because then it could be extended. but that can also be done at the time it will be used but i like the idea, i'm always using services in my applications, they make everything easier.
I used to call it "model". And for the application logic, I refer to it as the "controller". So for every use case in my apps, I usually have: /feature /__tests__ -> unit for model and integration for the UI /components feature.model.ts -> business logic feature.controller.ts -> application logic feature.ts -> UI index.ts
I also like to add behavior to domain objects so they encapsulate data and logic, but I guess that's the bias of someone used to object orientation haha
very nice splitting, and I see that every function in the domain is easily testable Nik Sumeiko
Interesting perspective! I’ve always felt that introducing domain abstractions like this can sometimes add a layer of complexity that smaller or faster-moving teams struggle to maintain. It shines when the domain has real business logic: rules, states, and invariants shared across features, but falls apart when the app is mostly CRUD or evolving quickly. Curious how you decide when it’s worth the extra structure versus keeping it simple. Thanks for sharing
Good night Nik! First of all thanks for sharing this kind of useful content. I find it difficult to differentiate between view models and domain model. What's the main difference? Are similar and can be mixed up?
Yep, having these Domain Objects or DTOs helps a lot. This way, we keep the responsibilities and it's much easier to maintain the whole project. Great tip, Nik Sumeiko!
React/Nextjs, TDD, Clean Architecture made simple
2w📌 Here's how the domain/ folder looks: domain/ bank/ BankAccountService.ts BankAccountAdapter.ts useSingleBankAccount.ts useBankAccountsCollection.ts The 𝘉𝘢𝘯𝘬𝘈𝘤𝘤𝘰𝘶𝘯𝘵𝘚𝘦𝘳𝘷𝘪𝘤𝘦.𝘵𝘴 will contain the shape of the 𝘉𝘢𝘯𝘬𝘈𝘤𝘤𝘰𝘶𝘯𝘵 domain object + all operations on it. The 𝘉𝘢𝘯𝘬𝘈𝘤𝘤𝘰𝘶𝘯𝘵𝘈𝘥𝘢𝘱𝘵𝘦𝘳.𝘵𝘴 will have functions that query and mutate domain objects or a collection of these. The 𝘶𝘴𝘦𝘚𝘪𝘯𝘨𝘭𝘦𝘉𝘢𝘯𝘬𝘈𝘤𝘤𝘰𝘶𝘯𝘵.𝘵𝘴 repository provides a particular domain object to its consumers, exposes mutation methods, and invalidates dependent Domain Objects.