3

I like domain driven design, and onion design. However I would really like my features to be encapsulated. If i check out some legacy code and I need to investigate some specific feature the best thing that I think could happen to me is that I see (lets say in java) is that there would be a package with featurename and all the feature functionality would reside in that package. In a way that if i delete the package the feature would be gone.

While I understand its impossible because some of the feature might reside in other service I'm trying to at least think if its correct to do that in my own service.

My problem with this is that usually in DDD and in onion design you have the domain which contains a bunch of domain objects. now some of these objects are of "my feature" and some of them are of other features. lets say (user authentication is one feature and user twitter account details is another feature).

Is there any design methodology that does something with it? is what I'm suggesting makes sense?

Example: you have a project that manages users waiting in line. You are asked to add a limit, max on the queue size. you need to read that max queue size from configuration. Then in your logic you need to apply that max queue size. You also need to expose this configuration of max queue size in jmx, also you have an api to that service so you need to expose that max queue size as an api in that service.

Current app packages: --> conf (deals with reading configuration) // do change here. --> domain // do change here for that small subfeature. --> logic // do change also here for that small subfeature --> service (where api are) // do also a change here.

so in the above simplistic example for a simple subfeature addition i need to make changes to at least 4 packages. Which means my subfeature is spread among these packages and classes.

I understand DDD mentions boundcontext but notice my feature here is very very small! just adding a max limitation on size. So it does not seem appropriate to open a boundedcontext on each such small subfeature.

So the way I deal with it today I go conf i find there a class named Configuration which contains various configurations. I add to that Configuration class a member named Configuration.maxQueueSize.

I go then to domain to model that new property I have there lets say Account I add to the Account class Account.maxQueueSize.

I go then to service an expose a new method named getMaxQueueSize

as you see I went to multiple packages and multiple classes already dealing with other functionality and extended them.

This is a very small feature, for one hand I cant imaging that for every small feauter I would open a whole new bounded context with its own service, domain, conf packages (and lets assume there are more) so that each feature is in its own package.

On the other way on each feature I need to extend configuration, domain, service, ... which means no single feature is encapsulated well enough so that I can just delete it or see a package which its fully resides in so that I can better understand the app.

0
1

If I have understood correctly, your 4 packages (conf, domain, logic and service) are layers in your application. These layers do not separate different "functionality" as you call it, but different concerns. As such, it is perfectly fine that you have to touch all 4 of those packages when adding a new function however small it may be.

If you would have to add a new feature which is unrelated to your current "queue management" feature, you could add a new feature package which contains all 4 of your layers, but the "max queue size" would belong in your current feature, I'd say.

1
  • I didn't understand the second section starting with "if you would have to add.."
    – Jas
    Feb 15 '16 at 12:42
5

What you are looking for is called Bounded Context in DDD.

Now, as you are realizing in your question, bounded contexts might share data. The best way to implement it that I saw is simply to duplicate the entities. So Accounting context has Customer, and so does Production have Customer. But even though they have same name, they have completely different meaning. It then becomes responsibility of the layer above (most probably infrastructure layer) to save and load them into same table. But from the point of individual contexts, the customers are completely separate. This then allows you to completely remove any bounded context from the whole application without affecting any other part.

As it relates to Onion Architecture, I would design each "feature module" as a "slice" through the onion where each layer is single library. Imagine cutting out a piece round cake. So you could have libraries Feature1.Domain, Feature1.Database and Feature2.Domain, Feature2.Database, etc.. between which would be relationships according onion architecture.

But there is a problem. As you might know, some modules might want to communicate between each other. That's where Bridging libraries come in. Simply said, you could have library Feature1Feature2Bridge. This library would rely on Feature1.Domain and Feature2.Domain providing abstractions for communication, and this "Bridge" library would implement that communication. Then, this library would be loaded only if both features are loaded.

5
  • 1
    I may be mistaken, but I think OP is referring to having features that apply to the project if present and don't apply to the project if not present, without creating problems in compilation. Please correct me if I'm wrong, OP.
    – Neil
    Dec 21 '15 at 8:17
  • @Neil What do you mean exactly? In that case, his question has nothing to do with DDD or Onion Architecture.
    – Euphoric
    Dec 21 '15 at 8:21
  • I mean, this answer seems a little vague in the context of the question. Could you clarify with a tiny example perhaps?
    – Neil
    Dec 21 '15 at 8:23
  • @Neil The answer is vague because the question is vague. This is about high-level design. I can't really go into any detail unless question provides more detail of it's own.
    – Euphoric
    Dec 21 '15 at 8:46
  • this is good for big feature but I was asking in example about small subfeatures with them I see myself changing multiple places.
    – Jas
    Dec 21 '15 at 14:39
0

When I find that all the changes I'm asked to make require tiny changes to several different modules, that usually means that the way I've split the project into modules is inappropriate.

To give an example which I believe is similar to yours, one of the services I work on converts documents from an old format to a new format. Whenever a new feature becomes supported in the new format, I have to go back into this conversion service and:

  • Add the parameters representing this feature to the model objects for old and new documents.
  • Update the logic for fetching the old document from the old database, so it gets the old version of the feature.
  • Add logic to convert the old version of the feature into the new version.
  • Update a sort-of-config file to say that the service is now using model version 1.11 instead of version 1.10.

In my case, all of this is a single commit to a single repository that's deployed as a single package, simply because that's how we chose to split up our packages. Yes, it's still (roughly) four separate files, but I don't believe that's a problem as long as that set of changes can be conveniently committed, reviewed, deployed, etc as a single cohesive unit (which it can if you have decent tools).

For instance, it sounds like your model objects are in a separate package from the logic that operates on them, which is probably unnecessary unless said objects have to be shared among many other services. That's even more true for your configuration file, as I can't imagine that being reused by other services (unless the configuration is horridly overcomplicated). It's also difficult to think of any scenario where having a service's API and its logic in two separate packages would be a good idea. I can think of some derivative products like API documentation which perhaps belong in separate packages (though ideally those would be autogenerated as part of your build/deploy process), but the actual API/logic seem like they should normally go together.

4
  • The "One commit per feature" was tried and found to be inadequate. For example, how do you handle subsequent changes to the feature after it is committed?
    – Euphoric
    Feb 13 '16 at 20:21
  • @Euphoric I assumed this question was specifically about the initial implementation of a feature, without worrying about any subsequent iterations. Of course you have to do multiple commits if you do multiple iterations. That's separate from the question of whether each iteration should be split into several commits solely because it involves version number and config files as well as business logic. Or in slogan form: I'm arguing for "one commit per change" rather than "one commit per feature".
    – Ixrec
    Feb 13 '16 at 20:25
  • Then your answer is not valid, because question is exactly about "separation of features" not "changes".
    – Euphoric
    Feb 13 '16 at 20:27
  • @Euphoric The question is about a change that takes four commits to implement despite all being for the same iteration of a feature and none of them making sense independently of each other. I agree that neither I nor the OP have been especially picky about our use of terms like "feature", but I don't think that terminology issue is relevant to the question or the validity of either answer. Nor am I particularly interested in it. If this still bothers you it's probably better to ask the OP what he wants or suggest some edits to correct our usage.
    – Ixrec
    Feb 13 '16 at 20:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.