Task (aka DomainServices) Layer NHibernate Transaction Management

Jan 6, 2010 at 10:17 PM

Hi All,

I'm in the process of getting familiarized with WCHM, and have moved my focus to the domain layer.

I have some questions/comments here that I'd like your feedback on. Any input would be greatly appreciated.

 

In WCHM.Tasks.ProfileTasks, we have the AddAssertion method:

 

 

        public void AddAssertion(AddAssertionDetails addAssertionDetails) 
        {
            addAssertionDetails.Validate();

            // TODO: Ideally we want a transaction here as we are potentially doing two updates.

            var profile = this.GetProfileByUserName(addAssertionDetails.UserName);

            var tag = this.tagRepository.FindOne(new TagByNameSpecification(addAssertionDetails.TagName));

            if (tag == null)
            {
                tag = new Tag 
                    {
                        Name = addAssertionDetails.TagName
                    };

                this.tagRepository.Save(tag);
            }

            // See if there's already an assertion for this tag/category combination
            var existingAssertion = profile.Assertions.FirstOrDefault(
                a => (a.Tag == tag) && (a.Category.Id == addAssertionDetails.CategoryId));

            // If not add it. If there is, do nothing further - there's no point returning an error, since the user has what they wanted
            if (existingAssertion == null)
            {
                var category = this.GetCategory(addAssertionDetails.CategoryId);

                var newAssertion = new Assertion
                    {
                        Profile = profile,
                        Category = category,
                        Tag = tag
                    };

                profile.Assertions.Add(newAssertion);

                this.profileRepository.Save(profile);
            }
        }

 

 

My question is in line with the TODO comments included. Specifically: assuming we want to go ahead and plug in NHib transaction support to this domainservice, what would be the best way?

My initial inclination is to try to utilize the SharpArch Contrib Transaction Attribute:

http://wiki.sharparchitecture.net/ContribTransactions.ashx

Do you think this is the best method?

Would this integrate well if we also had the SharpArch.Web.NHibernate.TransactionAttribute attribute specified on the calling controller method?

 

What kind of solution did you have in mind while architecting the layer?

Any input would be great.

Thank you in advance for your continued support!

Best regards,

Martin

Jan 7, 2010 at 2:06 AM

I was looking into this more, and it turns out there are a lot things to consider.

I've started a general discussion on the SA mailing list that may be of interest here:


Revisiting Transactions

http://groups.google.com/group/sharp-architecture/browse_thread/thread/6dde132d999c0160#

Jan 7, 2010 at 3:32 PM

It really depends, since the service layer could technically be used either by the Controllers (i.e. Web UI) or by another front end, it should not necessarily be responsible for Session or Transaction management.  I asked Oren this same question, "where should session management be in a non-web application?"   He suggested that if it is MVC it goes in the controller, if it is MVP or MVVM use the "commands" objects ideally.  As another situation, I often use a simple messaging architecture to send commands to my database, so when I do that I do it using a specialized message handler that sees nHibernate "command" messages and wraps them in a transaction, this is very similar to using an attribute, but it can be done without the attribute and has some other advantages.

So for this situation I would place the [Transaction] attribute (which you don't need contrib for) should go on the Actions in the controller.  If I was calling the tasks from a WPF GUI or a WCF service or something I would use #A-Contrib and the [UnitOfWork] attribute.

Jan 8, 2010 at 11:49 AM

I thought that a Task is a synonym for Application Services, not Domain Services!

Jan 8, 2010 at 9:45 PM
pietbeenz wrote:

I thought that a Task is a synonym for Application Services, not Domain Services!

pietbeenz, you have a point. The "services" concepts in DDD are woefully confused and used interchangeably.

The "ApplicationServices -> Tasks" section of the WCHM docs has a launch point link to a lot of discussion on the topic.

 

As far as I am interperting it, WCHM is set up like so:

WCHM Tasks Layer - A fusion of "DDD Domain Services" (stateless domain behaviour that encapsulates operations on more than one domain object) and "Application Services" (set of services that act as boundary to the domain layer, used by the client application)

WCHM Services Layer - (As defined by Domain.Contracts.Services) - An entirely different kind of "service". I like to think of this one as more akin to behaving like a Repository. It's a "service" that (implemented by the Infrastructure layer) is responsible for doing stuff with external resources (outside of the system). In WCHM, you will notice that the Services are consumed by the "Application Services" layer (one part of the "Tasks" layer, as above)

 

Ultimately, I really like the setup of WCHM in this regard. I think if you review the codebase you'll get a much better idea of how things fit.

Please let me know if you have any comments. I actually intend to do a wiki or blog post about this sometime, as it's something that's been nagging me for quite some time as well.

Jan 8, 2010 at 9:56 PM
chnicola wrote:

So for this situation I would place the [Transaction] attribute (which you don't need contrib for) should go on the Actions in the controller.  If I was calling the tasks from a WPF GUI or a WCF service or something I would use #A-Contrib and the [UnitOfWork] attribute.

Hi Chris,

Thank you for the comments.

What about if your Application Services layer (in the DDD sense - a gateway API to your domain/domainservices) needs to handle some aspects of session/transaction?

 

For instance, if I have a "Publish News Item" action in my app services layer, which needs to:

* Validate a domain object

* Save a domain object

* Register the new domain object with another Service

* Do some more processing on the new domain object

and needs to handle this workflow "transactionally"? I would think I have two options:

1) Handle the session/transaction stuff inline, within the action. This would require knowledge of the session and transaction

2) Handle the workflow without using session/transaction semantics. This would involve some kind of exception management contraction, where ultimately the action would pass a result back to the caller (say, a web Controller), which would then be responsible for taking the response of the action and doing the required commits/rollbacks to the session/transaction.

 

In any case, I thinks this falls outside of the scope of WCHM per se.

I started a discussion on the SA mailing list on this subject:

http://groups.google.com/group/sharp-architecture/browse_thread/thread/6dde132d999c0160#

I would love to get your input there!

Thanks Chris!

Jan 9, 2010 at 3:24 PM
martin_aatmaa wrote:

As far as I am interperting it, WCHM is set up like so:

WCHM Tasks Layer - A fusion of "DDD Domain Services" (stateless domain behaviour that encapsulates operations on more than one domain object) and "Application Services" (set of services that act as boundary to the domain layer, used by the client application)

WCHM Services Layer - (As defined by Domain.Contracts.Services) - An entirely different kind of "service". I like to think of this one as more akin to behaving like a Repository. It's a "service" that (implemented by the Infrastructure layer) is responsible for doing stuff with external resources (outside of the system). In WCHM, you will notice that the Services are consumed by the "Application Services" layer (one part of the "Tasks" layer, as above)

These concepts can indeed be confusing. My interpretation:

WCHM Tasks Layer -Just another name for "Application Services" which helped their development team to clear things up. Controllers always talk to one or more Tasks. If business/domain logic is involved then Tasks make use of one or more (Domain) Services, when there's no business/domain logic involved then they talk to one ore more Repositories. Tasks are part of the application and act as a boundary to the domain layer and external services.

WCHM Services Layer -These are Domain Services that serve one or more Domain Entities. These services are part of the domain and can live without the client application (in this case MVC) and domain/business logic should be placed in there. They provide logic that shouldn't be exposed by the entities themselves to keep them POCO and follow the SRP.

I'd welcome a description from the development team that shows the responsibilities of both layers to make this clear for once and for all.

Jan 9, 2010 at 9:01 PM
pietbeenz wrote:

WCHM Tasks Layer -Just another name for "Application Services" which helped their development team to clear things up. Controllers always talk to one or more Tasks. If business/domain logic is involved then Tasks make use of one or more (Domain) Services,

Your description is what I would call the standard DDD setup. I don't think WCHM is doing it this way though.

In WCHM, there are no such (Domain) Services. The Tasks do not use any (Domain) Services. The Tasks themselves /are/ the (Domain) services.

The only "services" defined in the Domain (Domain.Contracts.Services) are "external services" - stuff like INewsService, which is a contact for an external service, not a "domain logic" service.

 

WCHM Services Layer -These are Domain Services that serve one or more Domain Entities. These services are part of the domain and can live without the client application (in this case MVC) and domain/business logic should be placed in there. They provide logic that shouldn't be exposed by the entities themselves to keep them POCO and follow the SRP.

Where is this services layer in the WCHM source code?

The only layer that I see that conforms to this description is the Tasks layer.

 

I think part of the confusion may be due to the fact that the WCHM app is a gutted form of the original app. I wouldn't be surprised to find a real "Domain Services" layer in the full-blown app.

 

Just so we are clear, let's identify all the types of services we have been talking about:

 

* Domain Layer

** Domain Objects

** Domain Services - the DDD kind of service - domain/business logic that is not in the entities themselves, or involves more than one aggregate root entity

** External Services (contracts only) - Like repositories. These represent services that involve domain entities, but rely on external systems to produce their results

** Repository (contracts only) - Involve domain entities, but rely on external systems to product their results

 

* Application Boundary Layer

** Application Servies - the DDD kind of application service - a entrypoint/API for clients/consumers of the domain

 

In summary, by looking at the code, WCHM has:

* External Services - the "Domain.Contracts.Services" namespace, implemented by "Infrastructure.Services"

* Application Services - the "Tasks" namespace

It does not have discrete implementation of any "Domain Services". This is either due to omission in the sample app, or due to concious choice (ie, to fuse the "Domain Services" layer with the "Application Services" layer - resulting in the "Tasks" layer).

 

As you mention peitbeenz, at this point it would be best for the authors to chime in. :D

Developer
Jan 12, 2010 at 9:01 AM

Hi guys,

Sorry for being late to the table on this one. This point was probably the most discussed point throughout the lifetime of the project - within the development team, with other colleagues during "show and tell" brown bag sessions, on emails/discussions etc etc. I think the point is that, as Martin states:

"The "services" concepts in DDD are woefully confused and used interchangeably."

At the start of the (original) project, our team was fairly new to DDD which made things even harder to get to grips with. To add to this, the S#Arch docs themselves were slightly vague on the use of the Application Services layer and we ended up going round and round in circles on this one.

Martin has explained the set up in WCHM correctly, but for clarification we have:

Domain Layer

- Domain Objects (entities)

- External Services (contracts only) - Like repositories. These represent services that involve domain entities, but rely on external systems to produce their results

- Repository (contracts only) - Involve domain entities, but rely on internal systems to product their results

Tasks Layer

- Tasks - a fusion of the DDD Domain Services (encapsulates operations on more than one domain object) and the DDD kind of application service (entrypoint/API for clients/consumers of the domain)

How we got here is probably worth explaining...

The renaming of Application Services to Tasks was purely to remove confusion over the word "Service". Our application boundary is not implemented as any kind of service (although it easily could be), but we wanted to get away from that perception and remove one of the Service definitions in the code. Whilst this is not strictly a DDD concept, the use of language in DDD is highly important and we found that this simple name change eliminated a lot of the discussions immediately - it seemed to make sense straight away. For the record, this idea originally came from a course I attended by JP Boodhoo, who always called his application boundary layer "Tasks". 

The lack of a Domain Services layer is a bit more haphazard than a deliberate conscious decision. In our original application at the beginning of the project, due my misunderstanding, I tried to mandate the use of the DomainServices, but this resulted in a bunch of classes that didn't really do anything other than just hand off to the repositories. We only really had a couple of places where the use of this extra layer could be justified, e.g. a Catalog class, that provided business functionality around Products and Categories (domain entities, different aggregate roots). Which gave us:

Controller -> Tasks -> DomainService -> Repository

New people joining the team questioned the use and value of the DomainService layer and the natural response seemed to be, why doesn't this go in the Tasks layer. My original intention had to been to set out to "do DDD properly", i.e. follow the definitions by the book, however, in practice we ended up taking a lot more pragmatic approach of dropping this and began to add business functionality into the Tasks. It just seemed to be easier for people to understand.

So, as Martin assumes, we did have some classes that would fit into this definition in our original application.  However, over time, we migrated towards adding this logic into the Tasks, giving us:

Controller -> Tasks -> Repository

WCHM is a much simpler application, and our thinking around the Tasks had evolved by this point into seeing it as a fusion of "DDD Domain Services" and "Application Services".  So, no DomainServices in WCHM. If I was starting our from scratch again, I'd probably stick with this approach - it seemed to work best for us.

The other thing I went back and forward on a bit was around external services. Ultimately these are just other repositories of data, regardless of whether we own it, or if it comes from somewhere else. So, our original application has some external service implementations called IRepositories. However, this wasn't ever mandated and we ended up with a few IService, and some IRepository. In WCHM we're consistent - external data sources are IService and any sources of our own data (e.g. SQL and FNH) are IRepository, but part of me would like to change them all to be IRepositories and let the implementations handle *how* the data is accessed. That's probably another discussion...

Anyway, I hope all this helps and clears some of it up. Happy to be involved in further discussions around this...

Cheers

James

 

WCHM Tasks Layer - A fusion of "DDD Domain Services" (stateless domain behaviour that encapsulates operations on more than one domain object) and "Application Services"