1

I have a frontend project using VueJS and TS and I structured it using DDD. So far, I have some modules (bounded contexts, domains - use the term you prefer) which are used in two ways: some modules will have both views and widgets or only one of those, where the views have a route associated and the widgets are like "attachments" to views. For exemple, you can have the module for managing the kitchen of a hotel and, within it, a view for the CRUD of chefs; also, in the same module you can have a widget (which will operate within a modal) to see all meals prepared in a range of time. The view has all operations enclosed within it, but the widget usually will emit a selected record (in the example, a meal), so that another module can use it, let's say the module for managing bedroom requests, where you could select a meal for delivering to a bedroom.

Since those widgets are like attachments to serve other domains/bounded contexts, I thought of simply emitting the selected entity, but I've been advised against it because of strong coupling. An alternative recomended to me was the "published language" pattern, where I'd have DTO exporting the data I need instead of using the entity directly, so that a domain's entity won't escape its boundaries.

My biggest fear about this solution is that it seems a bit "too much". I mean, some modules are going to be complex and big - the appropriate scenario for a DDD approach-, but the widgets will usually be a simple CRUD, or even a listing, so it seems like I am overengeneering them. On the other hand, I know how big this system is going to get since I am migrating it from an already existing project, and it seems to make sense that every part of the project, even the smallest, follow the same principles: it would make every part of the project familiar, follow a single pattern, and be ready for extension when needed.

What's your take on this?

If more context is needed, let me know.

4
  • What makes you think DDD is a good choice here? Commented 11 hours ago
  • (This is a classic example of why StackExchange should show tags above the question body: the question is pretty meaningless if you don't know what DDD stands for in this context, and it's long enough that in many cases below-question tags won't even be visible unless/until you scroll…)
    – gidds
    Commented 11 hours ago
  • @StanislavBashkyrtsev, the initial reason I started with DDD in frontend was because most code I read was just html+css+some js without much structure. Since this is the most complicated and business-rule-rich project I've ever worked with, DDD seemed like the best design to try in the frontend because of separation and encapsulation of contexts and because it forces you to think in terms (language) of the business. Commented 6 hours ago
  • I see.. Well, my advice is to try it on a small part of the project. Otherwise you're risking to seriously damage it. This is your 1st attempt at DDD, so you may not fully understand what you're doing. And you don't yet realize that DDD works only for simple projects (github.com/ctapobep/blog/issues/18), and for complex cases it leads to overengineering. My advice is to look at complicated projects that are well structured and written by seasoned engineers (Postgres, Linux) and learn from them. Or better start with algorithms (take Sedgewick course) - they teach to write simple code. Commented 1 hour ago

3 Answers 3

2

I think you need to lean on intuition and past experience. You already have a system to base your decisions on. It feels like unnecessary work now, but the real cost comes later when you want to change things. Yet, you've got to balance all this with deliverables and due dates. Here is how I would approach this problem:

  • For simple components and parts of the system that have seen little change over the years, consider passing the entity so you can just be done with it.

  • There's also nothing wrong with defining an interface that acts like a DTO, which is implemented by the entity. Sure, it's a little extra development, but this adds nothing to the file size in Typescript; interfaces are stripped out during the build process. This is a good compromise for parts of the system which are likely to change. This isn't a bad place to start for the stable parts of the system, too.

    • Regarding the parts that don't change often: sometimes components don't change much because earlier design mistakes make them harder to change. Sprinkling in a little decoupling now might reduce the cost of future change, thus making these parts change more often as time goes on; this is a good thing.
    • The most frequently changed areas of an application tend to be where the business rules are understood poorly and need to be revised repeatedly, where business rules are emerging, or where components were designed to facilitate testing. The easier code is to test, the more likely it is to change.
    • Components that do or should change more often will benefit more from decoupling, so the investment now will pay dividends later when changes are requested.
  • Learn lessons from the old application. You are rewriting this thing; why? Don't make the same mistakes. Choose a design for components that mitigates the problems that prompted the rewrite to begin with.

  • Even if you decide passing entities around is the way to go, make sure the structure facilitates writing tests. An easily testable application is a changeable one.

While domain-driven design doesn't provide an architectural scaffolding for your application, it does prescribe clear boundaries between modules. The Dependency Inversion Principal is relevant here. I think your system would benefit from some form of abstraction between modules, but that can be as simple as events passing around an interface that your entities just happen to implement. That's probably where I would start, and then introduce additional classes with relevant mapping when new types and the boilerplate code to map between them solves more problems than it creates.

1
  • I think I am going with the DTOs. The original project is older than me and was done in Delphi a long time ago. Paradigm is changing and I am wirting its first web version, so there's a lot of changes, including UX changes done mainly in the older, long forgotten modules that haven't seen refactoring for a while. The project has about 50 modules with each having subdomains inside them, so... better to do things right early. Commented 21 hours ago
1

How large is the team working on this project?

If you are working alone on this, or with a small team of three or four persons, it is usually most efficient to stay pragmatic and avoid writing, debugging and testing any avoidable boilerplate code. Don't do DDD for the sake of doing DDD for each and every minor widget, only do this to the modules where you think it will pay off. Since you know the details and size of each part of the system, I assume you can more or less easily find the sweet spot for this, and you do not have to defend your architectural decisions against a group of 30 other people.

On the other hand,the larger the team is (or when multiple teams are involved), the more important standardization of the overall architecture becomes. With a larger team, maintaining extra layers and more boilerplate code right from the beginning is usually more affordable than introducing this afterwards. Smaller modules can serve as examples for junior devs in your team, which will use them as a template for larger modules. The common structures will help team members to switch from one part of the system to another which they are not that familiar with, and when a small module evolves into a larger module, there is no general need to teach juniors when the point of introducing a DDD layering is reached (which is often opinionated and can lead to unproductive discussions).

2
  • Currently, it's just me, but in the future anyone from my company could join or hop in, which would be about 7 more people. Commented 20 hours ago
  • 1
    @BernardoBeniniFantin: think big, start small. As long as your team is small, keep the system lightweight, but care for vertical decoupling (separated bounded contexts with clear interfaces between). When the team grows, you can give new team members the task to restructure certain modules in a more rigorous and DDD-like manner before extending it.
    – Doc Brown
    Commented 20 hours ago
-1

My biggest fear about this solution is that it seems a bit "too much".

As a rule, the parts of your data model that are managed via CRUD don't naturally lend themselves to a "domain model" approach. See Udi Dahan on functions, databases, and services; the cognitive load of a "domain model" is really only justified in the services case.


It would probably be worth while to review Gary Bernhardt's talk Boundaries.

I've been advised against it because of strong coupling.

You aren't going to be able to completely escape coupling; as soon as you decided on a design with two different modules (the widget and the entity) that exchange information, you are locked into coupling -- these components need to have a common understanding of how information is exchanged, and compatible understandings of how information is represented.

The more significant question is how tightly coupled that "contract" is to other decisions that you might want to change (this is the big motivation for "information hiding"; see Parnas 1971).

Of course, being tightly coupled to a contract that is unstable has its own problems.

My biggest fear about this solution is that it seems a bit "too much"... it seems to make sense that every part of the project, even the smallest, follow the same principles

Yup - and here's the bad news -- choosing a single "pattern" to use everywhere is a sort of compromise; you are going to find that it is inefficient in some contexts. Welcome to the wonderful world of tradeoffs.

What I would suggest you do is take a bit of time to document the information you gathered and the conclusions you reached, so that you have a "paper trail" to help you when you later need to consider changing things.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.