5

At present we have 2 PHP applications which share absolutely no code, but share a common database. One app is the timesheet application for administrators, the other is the timesheet applications employees log their time against projects. We are proposing a complete rebuild of the application using Spring Boot, Spring Data Jpa, Hibernate, Java8. We want to keep the applications separate as business may want to deploy and make changes to them independently, and they also present and take different data so it doesn't make sense for them to share the same models and API anyway.

The applications doesn't have a lot of complex business rules yet, the system is half data entry, and half reporting. However over time there is more functionality that is being handled manually that we want to being into the system. As such I would like to borrow some of the ideas of DDD and CQRS into the design.

My proposal is a multi module Maven solution

-- Timesheet (parent module)
  -- timesheet-core
    -- domain services
    -- repositories
    -- domain models / jpa entities
    -- other common logic and classes
  -- timesheet-web-admin
    -- controllers / rest endpoints
    -- services (will query from here for read operations, writes go through repositories)
    -- DTOs (used to move data between client and other layers)
  -- timesheet-employee-admin
    -- controllers / rest endpoints
    -- services (will query from here for read operations, writes go through repositories)
    -- DTOs (used to move data between client and other layers)

I'm not one who believes in strictly following patterns like a doctrine / religion. I think DDD and CQRS ideas and patterns might be beneficial though. We have roughly 100 - 120 people using this system so I'm not sure if event sourcing and having separate databases is really necessary.

Does this design / architecture at least make sense from a high level or am I making a terrible mistake trying to bring DDD into the fold? My idea is JPA / Repositories will handle all write operations, but the appliation services will handle reads and returning the data in DTO objects.

enter image description here

5
  • I believe, just like @DavidPacker, that CQRS works. I recommend that you take a look at the hexagonal architecture that works very well with DDD and CQRS. Also, you could take a look at microservices as they permit to work independently at separate domains and promote loose coupling between them. Mar 2 '17 at 18:35
  • @ConstantinGALBENU We looked at the Microservice approach but we were worried about data consistency, although I suppose all the microservices could share the same database. If they had separate databases there would be another layer of complexity syncing data between the services
    – greyfox
    Mar 3 '17 at 13:48
  • no, the microservices must not share the same database, it would be against the main purpose of loose coopling Mar 3 '17 at 14:10
  • Would something like JMS be sufficient for communicating between micro services? Say information is created in Service 1 that Service 2 needs to know about to persist and update its database. Could you just fire a JMS message to ActiveMQ or something like that?
    – greyfox
    Mar 3 '17 at 17:11
  • If you've read the blue book, you will probably be familiar with the phrase "Don't fight the framework." If you use JPA with a pure DDD project, you will be fighting the framework. It really depends on how diligently you apply the concepts. I would recommend taking what you need from DDD, as you said, and not falling down the rabbit hole on this project, lest you find yourself trying to implement value objects with a persistance framework that needs an \@Entity and an \@Id in order to persist. Not saying it is impossible, but it definitely involves workarounds.
    – Nate T
    Jun 22 '21 at 23:45
2

I am currently working on an application that also does some time reporting. It has never appeared to be a "just data entry and reporting" application.

DDD or not is not what you decide by looking at the solution space alone. You should get yourself out of the developers comfort zone and drag yourself to the problem space, start talking to your customer meaning they are your domain experts. Or this might be the product owner. As soon as you stop discussing data entry and visualisation, you might be getting somewhere.

If you use techniques like Event Storming when holding these discussion, you might discover some behaviour around your domain, and it might be actually quite complex. Some might say this is unnecessary complexity, but make no mistake - accidental complexity is completely different from the actual domain complexity. Of course, some edge cases might not require automation and can be happily resolved by simple human intervention, but some might be very frequent.

One thing to think about is the audit log, typically people want to see how time entries flow through the system. No body wants to see their timesheet amended by someone else without knowing why, when and why.

The approval process could be quite complex as well.

What I mean is that your solution seems to be purely technical. This is not DDD. DDD is about Ubiquitous Language and Bonded Context, not about DTOs, services and repositories.

4
  • At first I thought CQRS would be completely over blown for this sort of application, until I realized it was a time tracking app. Audit logs. Nuances to how different groups log their time. Oh, and don't forget those old timers with union contracts stipulateing that every third Tuesday is double time after 5 hours of over time the previous week. We had a similar system and every contractor laughed at us when we investigated replacing the system. Mar 7 '17 at 3:26
  • This project may not need a rich domain model. It looks like a simple timesheet app. I could be wrong, but if I am not, Simple apps, especially from newer developers, can quickly become overly complicated. Or maybe overdeveloped is a better term. I see DDD projects as putting in the extra effort up front so that the project can grow properly in later stages of the SDLC. This one my not need it. seems to be mostly infrastructure/mvc boilerplate with not much model. That said, I am just going by what I see, and I could be way off. Hopefully this doesnt seem in disagreement. Points are spot on.
    – Nate T
    Jun 23 '21 at 0:02
  • Ran out of space, so I did A LOT of clipping. Hopefully I didn't clip the meaning out of it. If the wording seems off, that is why... I was basically making the point that Evans makes at the beginning of his 1st book. Which projects benefit... That said, I agree completely, on the condition that this project is destined to grow.. Too much comment! :O +1
    – Nate T
    Jun 23 '21 at 0:07
  • "It looks like a simple timesheet app" - time reporting is quite a complex domain. I've seen and used time reporting applications that are built like "hey, it's a simple db table with UI on top", and they were all awful to use. Don't many any assumption until you talk to the people who know the business rules. Jun 23 '21 at 6:28
0

CQRS can make sense anywhere. CQRS means you separate reads and writes in the application layer, to make them separate concerns, and that's it. Both commands and queries may use the same database, for CQRS alone you do not need to read from a different database than to which you are writing. You can do it, but it's not necessity.

It's event sourcing, which goes so well along with CQRS, that complicates stuff, because your aggregates are no longer objects but a series of events using which they are constructed. Once you start integrating event sourcing to your system then you might start thinking about using a different (maybe a document based) data store for reads to prevent the necessary need to re-hydrate aggregates on each request - but even then that might not be necessary at all. If you have event sourced aggregates which may reach only up to lets say 10 events, managing a separate data store is probably pointless, as hydrating 10 events within the memory itself is very fast anyway.

0

One issue with mixing DDD and CQRS:

If you were to use DDD as intended (as opposed to taking what you need) CQRS would not work, as this very principle sort of flies in the face of the "True domain model" concept.

In CQRS, much of the logic is pulled from the model and put into service packages. As others have said, commands are split from the Queries (the R from the CUD), and they often use separate (but synced) datasources along with command services and query services.

This isn't a law, of course. However, any CQRS project that I've ever been a part of has followed this basic structure.

By contrast, DDD calls for services to be kept within the model. Occasionally they are allowed to live inside their own objects, but this is only in instances when they are not functionality of any other model objects. Usually service methods belong inside a model class.

As of late, a lot of DDD proponents (including Eric Evans, supposedly) are against the concept of service modules altogether, as some feel they contribute to the anemic model antipattern.

It is also worth mentioning that CQRS is a form of AOP, which is (usually) thought of in terms of objects and OOP, although functional DDD is a thing as well.

In summary, I am not suggesting that these concepts cannot intermingle, just that if you choose to use them, take only what you need. CQRS is really only an idea, so you are in or out there. As for DDD, I would suggest using aggregates, at least. Be sure to try deciding what you are taking and leaving from the outset, if just for a frame of reference. This will definitely change as the project progresses, but it will help you to remain consistent.

If you haven't already, check out Chris Richardson's work. as these concepts both fit into a microservices programming style which he helped to pioneer. I am guessing you've heard of him, but this answer wouldn't be complete without homage to both Richards and Evans.

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.