Just one maven artifact?

120 views
Skip to first unread message

Marcin Kuthan

unread,
Sep 2, 2011, 3:20:08 PM9/2/11
to ddd-cqr...@googlegroups.com
I understand the reason of this compromise. But I faced some problems with project modularization - strictly connected to DDD patterns (and also testability). It would be really great to see the working solution in the leaven. Project modularization should be also important for DDD technical infrastructure example.

To be more precise. I wanted to separate domain model from infrastructure. I do not need to rebuild (and test) infrastructure when domain is modified. In the domain module, only pure unit tests are executed (with mocked infrastructure) which is extremally fast. The infrastructure is tested within spring context, embeded database or JMS (still executed as mvn test) which is slow. When the modules are separated the development is easier (you can easily run junit test for whole eclipse project), it also helps to get quick feedback from continuous integration build. If you align to DDD principles correctly, you will spend most of the time on domain development - so make it developer friendly.

My first assumption was to move persistence.xml, named queries and metamodel to infrastructure module. They should not clutter domain module. But how to generate JPA metamodel when domain sources exists in another module? You can unpack sources from another Maven module (http://www.sonatype.com/people/2008/04/how-to-share-resources-across-projects-in-maven/) but it did not work for me due the the several mostly voted plugin bugs (http://jira.codehaus.org/browse/MDEP-194, MDEP-98, MDEP-187). 

I gave up and put persistence.xml, named queries (externalized to xml) and metamodel in the domain module :-( So the project modularization in DDD project is not always feasible in one hour ... 

I think, in the minimal project configuration at least: app-domain, app-infrastructure and app-webapp modules should be defined. In more real world scenario each bounded context should be separated, and also infrastructure could be divided (db, ldap, jms, etc.).

What do you think about "small" project modularization for better DDD projection (three modules)? It would also help in planned milestones (additional UIs).

Sławomir Sobótka

unread,
Sep 2, 2011, 3:49:07 PM9/2/11
to ddd-cqr...@googlegroups.com
Modularization is... difficult...
And You are right... one hour mentioned in wiki is just a
encouragement for daredevils:)

Partial modularization is worthless in my opinion.
We would also need consider:
bounded contexts - maybe together with application layer (app services/handlers)
above should have impl and api(events, some dtos, interfaces)
sagas - now we have Sage in scope of one module (BC) but in general
they should span above many BDs

I thought about this issue, we have even started with full-blown
modularization. So all work is done.

But trust me - show this to Maven newbee and he will cry and wont look
at Leaven any more:)
besides: Maven has some haters too:)

So to please everybody I'm thinking about providing "arhetypical"
project structure, but empty. Code would be maintained in current
shape - big pail and whoever wants can split it to modules. But it's
just a thought - every time I speak it loudly it sounds silly:)


btw: according to work optimization look at current approach: unit
tests are stored in main project, by integration and acceptance tests
are stored in separate project.

Marcin Kuthan

unread,
Sep 5, 2011, 10:33:30 AM9/5/11
to ddd-cqr...@googlegroups.com
Thank you for explanations. I don't fully agree (Java architects should not be Maven newbee), but understand your point of view. What is still worth considering:
1. Add project modularization or archetype creation to one of the milestones.
2. Set up new chapter in the documentation with general ideas how to modularize the project. And wait for the feedback from real-world projects created based on the leaven.

btw: test optimization - I would keep test code as close to the sources as possible. But for more complex test configuration (e.g: Selenium) the separate module is preferred way. You can even separate Selenium Pages (testing API) from test cases :-)

Rafał Jamróz

unread,
Sep 5, 2011, 12:22:51 PM9/5/11
to ddd-cqrs-sample
I agree that modularization is a great feature, but it's also very
unforgiving :)

In order to fully implement a bounded context (and properly 'bound'
it) you'd need at least 2 modules for each BC (like Sławek said), i.e.
sales-api and sales-impl (and the latter can be further split into
sales-domain and sales-infrastructure, but I haven't tried that).

API modules consist of Commands and Finders interfaces with their Dto
[and some Events if it makes sense to propagate them to the outside
world] and these are the only objects that come out of the BC. All the
DDD buildinging blocks, CommandHandler-s and Finders implementations
are hidden.

The API is used both in communication with the interface (in our case
a .WAR module, and possibly other, remote clients) as well as between
different BCs.

A short example (which is more complicated case than the what is
currently implemented in the sample)
In SALES business context I create an order for a client and I need
person's address. I cannot access Customer entity from the CRM module
because it's different BC so instead i query CustomerFinder from CRM
module to give me CustomerDto and use it's address.

If I need to keep track of that Customer in SALES (and keep additional
data with it) I would create another Entity and perhaps call it Client
and keep the id of the Customer (which I got from CustomerDto).

SALES context can change entities of CRM BC but only using provided
API, i.e. issuing commands, or dispatching events that CRM or sagas
listens to,

In above example - even though we use a relational DB underneath we
cannot use foreign key constraints because we only keep IDs as numbers
(actually we can ;) ), but this way we gain some scalability potential
if we chose to split BCs over different data stores.

One last closing comment on the Client<->Customer duality. It doesn't
break the "ubiquitous language" rule and actually follows it
perfectly. Language is ubiquitous only in it's BC and concept mapping
across different BC is natural.

Modularization is a very interesting topic and I wish we could delve
deeper and figure out how to do it properly in one of the next
milestones. Keep the ideas comming :)

Sławomir Sobótka

unread,
Sep 5, 2011, 3:38:19 PM9/5/11
to ddd-cqr...@googlegroups.com
It's worth to mention that Client/Customer could be implemented as a
common artifact for all bounded Contexts and could "live" in Shared
Kernel (one of Eric Evans techniques). And maybe in this case it
should be done this way. (But for example when You look at Party
Archetype for CRM domains it so complex that I don't want all this
"party stuff" in Sales)

But just in sake of education purpose we assumed that model of people
is very distinct in all Bounded Contexts and provoked all this
"problems" to illustrate them:)

Marcin Kuthan

unread,
Sep 6, 2011, 3:55:35 AM9/6/11
to ddd-cqr...@googlegroups.com
W dniu poniedziałek, 5 września 2011, 18:22:51 UTC+2 użytkownik Rafał Jamróz napisał:
I agree that modularization is a great feature, but it's also very
unforgiving :)

In order to fully implement a bounded context (and properly 'bound'
it) you'd need at least 2 modules for each BC (like Sławek said), i.e.
sales-api and sales-impl (and the latter can be further split into
sales-domain and sales-infrastructure, but I haven't tried that).


You need separate api / impl modules only if the modules will be released separately. If not, single module for BC is sufficient and another one for shared kernel (and perhaps sagas) e.g: leaven-common.

 
API modules consist of Commands and Finders interfaces with their Dto
[and some Events if it  makes sense to propagate them to the outside
world] and these are the only objects that come out of the BC. All the
DDD buildinging blocks, CommandHandler-s and Finders implementations
are hidden.

Why do you try use Maven modularization for hidding something (evil developer is still able to declare and use API from impl module)? If it is really needed use OSGi ...
In my experiences maven modularization has been driven by release management and Maven configuration simplification (set of dependences, build complexity).
 

The API is used both in communication with the interface (in our case
a .WAR module, and possibly other, remote clients) as well as between
different BCs.

Remote client for one BC could be a part of this BC.
 

A short example (which is more complicated case than the what is
currently implemented in the sample)
In SALES business context I create an order for a client and I need
person's address. I cannot access Customer entity from the CRM module
because it's different BC so instead i query CustomerFinder from CRM
module to give me CustomerDto and use it's address.

Declare CustomerDto in common module. Please note, that you can not refer to BC from common module which is really good. CustomerDto should be BC unaware, only assembler located in specific BC will be able to construct DTO with the BC specific data. 
 

If I need to keep track of that Customer in SALES (and keep additional
data with it) I would create another Entity and perhaps call it Client
and keep the id of the Customer (which I got from CustomerDto).

SALES context can change entities of CRM BC but only using provided
API, i.e. issuing commands, or dispatching events that CRM or sagas
listens to,

Use well documented interfaces declared in BC :-)
 

In above example - even though we use a relational DB underneath we
cannot use foreign key constraints because we only keep IDs as numbers
(actually we can ;) ), but this way we gain some scalability potential
if we chose to split BCs over different data stores.

One last closing comment on the Client<->Customer duality. It doesn't
break the "ubiquitous language" rule and actually follows it
perfectly. Language is ubiquitous only in it's BC and concept mapping
across different BC is natural.

Modularization is a very interesting topic and I wish we could delve
deeper and figure out how to do it properly in one of the next
milestones. Keep the ideas comming :)

Sure, thanks!

Rafał Jamróz

unread,
Sep 6, 2011, 6:36:28 AM9/6/11
to ddd-cqrs-sample
> Why do you try use Maven modularization for hidding something (evil
> developer is still able to declare and use API from impl module)? If it is
> really needed use OSGi ...
> In my experiences maven modularization has been driven by release management
> and Maven configuration simplification (set of dependences, build
> complexity).

Hidding is the same as using privitate methods, evil developers still
can ... But it's a concept that communicate something (If you'r
developing UI or another module reading API code should answer 80% of
your questions about that module). Also api can have different release
cycle and it should be enough to import module-domain and module-
infrastructure maven artifacts with only runtime scope.

Beside that if you make a remote client for your app (let's in SWT or
Flex) you only need to import the *-api module. SWT will probably use
it as-is (cause it's pure java) and Flex maven module can then use it
to generate flex interfaces and DTOs.


> Declare CustomerDto in common module. Please note, that you can not refer to
> BC from common module which is really good. CustomerDto should be BC
> unaware, only assembler located in specific BC will be able to construct DTO
> with the BC specific data.

This was just an example :) In case of a customer it might be fine,
but usually you can't get away with moving an entity that is used in
multiple BCs to a shared context.

Marcin Derylo

unread,
Sep 6, 2011, 2:33:53 PM9/6/11
to ddd-cqr...@googlegroups.com
Hi everyone, those I do know and those I don't ;)

We've tackled the problem of modularization in a DDD-ish project some 2 months ago. The project structure we now have is (per BC, which, to be honest, is far to big at the moment and is currently undergoing surgery):

* API Maven module - defining the communication protocol between the service provider (our server app) and clients (our Flex client, other modules/BCs);

* "core" Maven module - not only the domain but also application services. This also includes interfaces for repositories, external services etc - the implementation are not here, though. This module of course has a dependency on API as it implements it. Here you will only find unit tests of course;

* persistence infrastructure module (currently just one as we only use Hibernate as persistence engine) - has a dependency on core as it contains implementations of repositories. Also you will find Hibernate mapping files, Spring configuration for the persistence engine etc. This one has a set of integration tests for the persistence layer + some unit tests for things like custom Hibernate UserTypes;

* a couple of separate infrastructure modules implementing external service/extension point interfaces from the core module. Those serve as adapters for 3rd party systems. Whether any of those are used depends on environment the application is deployed in. 

* system tests, just like in Leaven - those are under-the-skin system tests, not going from the UI (unfortunately we don't have those yet).

* multiple web app modules, assembling the core and infrastructure modules suitable for given deployment environment (that's not per-customer thing, rather per-set-of-applications that are delivered together into given installation - currently we have 2, each using different infrastructure adapters).

So far it works pretty well and I think is quite extensible.
Reply all
Reply to author
Forward
0 new messages