Hey Maikel, your questions are awesome!!! I couldn’t avoid writing a long reply, sorry about that.
I am moving all conversations to this thread: the above, the one in the cookies POC #2521 and the one in the enterprises images PR #2512.
JS and CSS include in the layout
“Is the long-term plan that everything is in the web engine and we don’t need darkswarm/all any more?”
To start with, I think we should keep common look&feel stuff (darkswarm and admin) in the main app as it is. Because all frontoffice domains will need the common UI code of the website, just like in the backoffice all domains will need the common UI code of Admin.
At some point in the future, these two common UI modules (website and admin) can become independent modules that other domains fetch as a dependency. For now, I’d keep everything where it is in darkswarm and admin in the main app. First we need to extract what’s not common.
“I’m worried that dependencies could be a problem (will we have duplicate gem specs in the modules?).”
In terms of external dependencies, each domain should have its dependencies and some dependencies will be repeated across domains, I don’t think that is a problem.
API structure related to domains
In terms of api endpoints and engines we can have two options:
- option 1 - engine name on the api endpoint - /entities/api/enterprises or /web/api/content/ or /shop/api/product (entities, web and shop being the domains identifying it’s API).
- option 2 - no engine name on the api endpoint - all engines make endpoints at /api like /api/enterprises /api/content /api/cart
imo, I think we should aim for the first option with domain name on api endpoints (see below)
“Do you mean /admin/api or just /api? I never thought about the mix of admin and customer code in the API before.”
Yes, great question. The first thing is: do we want to have one API or do we want to separate front office from backoffice (or maybe one API per domain).
For example for product, we need a frontoffice API for rendering products lists, product detail pages and, at some point, some search capabilities. On the other side, we need a backoffice API for product inventory management, product import, etc. In lager scale systems this is a no-brainer, you need to split. In OFN, I am not sure. I’d go for the separate APIs per domain, for example, /shop/product (in the shop domain) for front office and /catalog/product (on the catalog domain) for backoffice management. The problem with not splitting is that the single API becomes quickly complicated with all mixed usages. And, imo, this is worse than the (not much) trouble of separating the APIs from the start. Does this question make sense to you? What do you think?
If we decide to have one API only, everything under /product (for the example above), I don’t think we should ever make the API a domain/engine. Domains should represent business domains and never technical parts of the application. I’d not follow Spree’s structure of having the api engine. Each of OFNs domains should implement the part of the API that belongs to it’s bounded context. Each engine will have it’s own models, controllers, views, api and angular UI, including some rails views and it’s html controllers as well as api controllers and angular code.
Maikel said: “My approach would probably be to first think of the best API. If it makes sense to have domains like shop and web in the API then it would make sense to structure our code accordingly. But in the end, the API should be something stable that is independent from our internal code organisation. So our code structure should not define the API.”
There is a major point to be made here. This is why I insist on calling them domains and not just engines (engines is what we use to implement our domains). Domains are not internal code organization. That’s why they are called business domains. In this specific case for example (enterprise images), it’s not just about the best API, it’s about where this API belongs: are images being managed from the backoffice and will only be something for the backoffice or are these images going to be served in a CDN and in the frontoffice. This is a factor that should be considered in every API endpoint.
Still regarding dependencies, Maikel said : “You mentioned that engines would have their own models. That was one of my questions about this structure. Wouldn’t that lead to a lot of code duplication? I thought the idea of models is that they represent the data to manipulate which is independent of the perspective from which we look at them.”
No code duplication at all. The challenge is to find the right place for each model. That is what DDD is all about. After you find the best place for the model, all the other places in your application that need that data will have to depend on the domain that holds that data.
For example, in the backoffice, if we have entities domain (for the management of enterprises, customers, etc) and the catalog domain (for the management of product and inventory), when you build the product management screens, for example, the data related to the enterprise of the products you are managing must come from the enterprises domain. From the world of “everything in OFN can depend on everything else” we are growing to a more organised structure, but to make that step, we need to think better about our dependencies. Anyway, we are still in the theory stage, we need to actually do it, to find out the best approach.
“we will have engines that share the same models… Spree came up with the core module. Should we do the same?”
I’d never create a module called Core like in Spree because it’s a meaningless name for a bucket where all the rest of the stuff ends up and no-one really knows what’s in there.
Maikel said: “And what if we have five domains and only two of them want to share a certain lib file? Do we put it in core or create another shared lib? I’m wondering if this approach will make us discuss all the time where code belongs.”
By definition, two domains should never own the same data. You need to decide in what domain each entity of your model belongs, in doubt, you put it in one of the domains and make the other depend on it. Only one owner, always. Imo, the discussion of where to put things is a good discussion and much better than having only one large bag of everything. The best part of it, there’s never a right place/domain to put stuff, we will see how our domains evolve.
In our specific case, to start with, I think the most important rule for dependencies is something like these Types of Dependencies between Domains:
RED - dependencies you don’t want to have, they mean your domains are entangled
YELLOW - they show that your domains are dependant but the dependency is contained
GREEN - they show that your domains are dependant but the dependency is very clear
And this is what I think falls into each of these 3 categories:
RED : dependency to Models of other domains
YELLOW : dependency to Services of other domains
GREEN : dependency to API endpoints of other domains with very clear contracts
If you need to use the DB or the model of another domain, you should try to use it’s API, if it’s too much trouble, you should use a service on that domain to fetch that data. If you can’t do that and you want to use the model of that other domain, you can still do it!, but you are keeping the two domains entangled and you are staying in the world of “everything depends on everything”.
This is for code related to business and data, for utilities (and the UI components for example) I think we should leave them in the main app for now (domains will depend on the main app for now), and at some point we can start to create shared libs outside the domains/engines.
Well, I hope this is useful. There’s no silver bullet and we will run into issues for sure. We need to go through the questions, go through the experiments and learn