-1

Background: I'm building a prototype scientific app with the "Exploration" and "Optimisation" parts. (Other parts might be needed later.) Each part uses functional reactive programming (FRP) and has its own GUI window. "Optimisation" depends on the user's choices within "Exploration"; there's no dependency the other way. Although the language is Scala, integration with Python, and possibly R, will be needed in future.

First, I've built an "Exploration"-only app based on the "onion" (clean) architecture and DDD principles. (The app is implemented as a multi-module sbt project in Intellij; see Figure 1.) I'm happy with this individual app.

"Exploration"-only Project. "Onion modules" shaded. Omitted dependencies: Main on every module; every module on Utils

Figure 1: "Exploration"-only project with the "onion modules" shaded. Omitted dependencies: Main on every module; every module on Utils.

Problems started when adding the "Optimisation" part to the same Intellij project. In DDD terms, "Exploration" and "Optimisation" are distinct bounded contexts. Combining both in one project would require having two "onions" side-by-side. The complexity is noticeably greater than with the "Exploration"-only project.

Tried: I've looked into microservices, which seem attractive, e.g., to separate bounded contexts https://www.infoq.com/presentations/ddd-microservices-2016, but also have drawbacks https://adamdrake.com/posts/2017-05-20-enough-with-the-microservices.html; https://martinfowler.com/bliki/MicroservicePremium.html. As I've little experience with them, they're probably an overkill for this medium-size prototype, but may be useful in future.

My current idea is to split the project into four separate multi-module projects; see Figure 2. The independent "Core Project" contains all the support domains and the abstract core domain. The "Exploration" and "Optimisation" projects depend on it as an external project. The "Main" project, dependant on the other three, wires the whole application and is responsible for passing data from "Exploration" to "Optimisation".

Question: Is this solution reasonable and how could it be improved? Or is there a better solution?

Related: Several SE questions suggest using microservices, e.g. How can you separate a monolith into domain-driven libraries without duplicating interfaces and still keep dependencies simple?. There're also language-speficic questions, e.g. Microservices and shared libraries for Python.

Split project. Between-Project dependencies (dashed lines) go upwards

Figure 2: Split into 4 external projects. Between-project dependencies (dashed lines) point upwards

  • What are your criteria for making a decision? – Robert Harvey May 7 '18 at 16:29
  • @RobertHarvey I'd like something leading to: (1) clean, well-organised and modular code and (2) rapid development of a working prototype. If the prototype is successful, I'd like some use for its code, rather than starting from scratch. Unfortunately, I don't have enough experience and information to assess, and even to know, the alternatives. – Tupolev._ May 7 '18 at 16:59
  • Why do you need microservices? Why not just use ordinary classes? – Robert Harvey May 7 '18 at 17:05
  • @RobertHarvey I was influenced by E. Evans' talk on separating bounded contexts by using a microservice per context. But I do think microservices are likely an overkill, at least now, and this is why I put "without microservices" in the question. But I might be wrong, eg, if there's a good MS framework. In any case, would ordinary classes within a "catch-all" project be enough? Or shouldn't I at least define an sbt multi-module project per context (as per Vernon's DDD advice)? – Tupolev._ May 7 '18 at 17:26
  • 1
    I believe that the organization of your project from a file and folder perspective should not necessarily correspond to DDD sensibilities. Same with microservices. Microservices should be chosen, not because DDD needs them, but because your implementation could benefit from the features that microservices provides. – Robert Harvey May 7 '18 at 17:30
0

I see a lot of conflation in your question, so first a little background: Bounded contexts (BC) separate areas of concern in a domain model and clear up confusion in a large system. A BC is anemic(ill-defined) if you don't have a context map. Logical separation exists because you have to translate concepts in one bounded context to other concepts in another. The biggest help comes when you have to map a term in one bounded context to the same term in another bounded context.

In implementation, you'll be using dependency inversion, services and domain events to decouple your domain from your platform. GUI frameworks, Web Services and Databases are all their own domains that are separate from your business domain. It is the interaction of your business domain (problem domain) with all of these implementation domains (solution domains) that is one of the main culprits of accidental complexity in systems. This is why onion architecture is great because it puts these concerns front and center.

It sounds like you're trying to organize your code by introduce a lot of accidental complexity into your system. From your problem statement:

Problems started when adding the "Optimisation" part to the same Intellij project. In DDD terms, "Exploration" and "Optimisation" are distinct bounded contexts. Combining both in one project would require having two "onions" side-by-side. The complexity is noticeably greater than with the "Exploration"-only project.

You only need one onion. You can put as many web service entry points in a single onion as you want. Physical separation is about deployment, instances, scaling, coordinating with different teams, rolling updates, orchestration, etc. It sounds like you're "programming in the small" but worrying about "programming in the large" problems.

Think of your DDD concepts as your way of designing one small piece of your system. The onion architecture is at a larger scale that includes the entire system. You can use DDD on application (solution) domains in your system to work out complex interactions, but that's a bit off the beaten path.

I think of web services more akin to actual DDD Application Services. They are more behavioral and encapsulated. Use Command–query separation web services to expose "read-only" views of your domain model for your interfaces. Bounded contexts can be represented as sub-paths under a single web service route if you want to go that way.

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.