Better Application Services and CQS using S#arp Architecture 1.0 Q3 2009

Updated 2010.03.09 to reflect small modifications that were decided through subsequent discussions on S#arp forum and other DDD posts.

Obviously, S#arp Architecture is the bee's knees when it comes to developing ASP.NET MVC applications. ;)  But as a project evolves and gets larger, "out of the box" S#arp Architecture 1.0 guidance runs into a few pain points.  Particularly, there's poor use of the application services layer, the separation between controllers and application services is not very clear, entity listing pages become performance bottlenecks as the domain model gets sizable, there is no command/query separation (CQS) "out of the box", and unit tests require re-occurring maintenance to deal with changes in the number of constructor parameters to controllers and application services.  While the amicable and adroit Alec Whittington (who is taking over the lead role from me on S#arp Architecture) is hard at work upgrading S#arp Architecture to accommodate recent dependency upgrades and accommodating ASP.NET MVC 2, I wanted to take a stab at resolving some of the architectural issues that I've run into, on S#arp projects over the past year.

I've developed and included a sample project, built on S#arp Architecture 1.0 Q3 2009, for the following key reasons:

  • To resolve some "pain points" that develop as S#arp projects grow to large sizes,
  • To demonstrate better use of the application services layer,
  • To demonstrate better command/query separation of the entity listing pages for dramatically better performance as the domain model grows,
  • To create an architectural spike for a new project I'm working on, and
  • To collect feedback from the S#arp community to determine if this is an appropriate architectural direction for the next release of S#arp Architecture.

Please post any feedback and/or suggestions you may have in the comments below or, more preferably, to the S#arp Architecture forums at http://groups.google.com/group/sharp-architecture.


Setup instructions

  1. Unzip the sample project to a BetterAppServices folder
  2. Create a new database called BetterAppServices
  3. Using SQL Enterprise Manager, run:
    1. /BetterAppServices/db/Schema/CreateBetterAppServicesDb_ChangesWillBeLost.sql
    2. /BetterAppServices/db/StoredProcedures/CreateGetProductCategorySummaries.sql
    3. /BetterAppServices/db/StoredProcedures/CreateGetProductSummaries.sql
  4. Open /BetterAppServices/BetterAppServices.sln with VS 2008
  5. In VS 2008, open BetterAppServices.Web/NHibernate.config and change the connection string to point to your BetterAppServices database
  6. Right click the BetterAppServices.Web project and "Set as StartUp Project"
  7. Run (F5) the project to see everything in action.

Changes from "out of the box" S#arp Architecture 1.0 Projects

This project is more of an architectural spike more than anything else at the moment. Accordingly, the CRUD scaffolding generator has been removed and non-essential unit tests have been removed to focus on the architecture itself.  Many of the changes will be incorporated into the S#arp CRUD scaffolding generator; this will either be available in the next release, or will be provided as an add-on, as this new approach does add complexity and introduces a major breaking change to existing 1.0 projects.

Major changes include:

  • /db Folder Changes
    • Added /db/Schema/CreateBetterAppServicesDb_ChangesWillBeLost.sql. This gets auto-regenerated when unit tests are run. The motivation was to have the DB schema automatically maintained while developing.
    • Added /db/StoredProcedures/CreateGetEntityNamePluralSummaries.sql. These SPs provide command/query separation for the entity listing (Index.aspx) pages, which frequently became a performance bottleneck.
  • Cross-Project Changes
    • Moved /BetterAppServices.Core/DataInterfaces/* to /BetterAppServices.ApplicationServices/DataInterfaces/. This is the only major, possible breaking change for existing applications. This was done to further remove the potential of domain objects using data repositories directly, and to allow the repositories to return objects from a new Dtos project for command/query separation, among other benefits (dicussed below).  Decided not to do this to support domain services which may require the use of repositories and to make upgrading from previous version much simpler in some cases.  Having the interfaces in .Core doesn't necessitate that domain objects use them; in fact, my rule of thumb is for all domain objects to avoid the use of repositories unless an exceptive case exists.
    • Replaced all uses of "using BetterAppServices.Core.DataInterfaces;" to "using BetterAppServices.ApplicationServices.DataInterfaces;" to support the above mentioned change.
    • Replaced all uses of IRepositoryEntityName with IEntityNameRepository. Be default, all entities now have an explicit repository. This avoids the need to manually change from the generic to the explicit, when the need arose, in unit tests and in constructors; which was frequently occurring.
  • /BetterAppServices.Core/QueryDtos (/BetterAppServices.Dtos)
    • Added a Dtos class library to provide an appropriate location for View Models and DTOs. While this project currently has a dependency on BetterAppServices.Core, so that the View Models can contain references to domain objects, this dependency could be severed for better separation between the view and the domain. The caveat is that much more work would be required maintaining a more complete DTO layer and transferring data via DTOs in all of the CRUD pages.  Decided that a separate assembly was overkill.  Consequently, added this namespace to contain query DTOs for transferring results of "report" queries into objects.  With this example project, only the listing page uses "pure" DTOs for much better performance. The other CRUD pages still communicate directly with domain objects to keep things much simpler. This is something that can be argued either way and it should depend on the project needs to determine if a more separated approach is warranted.
    • Added EntityNameDto.cs to act as a summary object to be bound to results from stored procedures (e.g., the entity listing pages), or other DTO needs.
  • /BetterAppServices.ApplicationServices/ViewModels
    • Added this namespace and EntityNameFormViewModel.cs to hold data related to adding and updating the EntityName. This object gets populated and passed to the entity form pages, accordingly.
  • /BetterAppServices.ApplicationServices
    • Added reference to BetterAppServices.Dtos so that app services can return DTOs.
    • Added EntityNameManagementService.cs. This app service class removes the CRUD logic from the controllers, makes the logic more reusable, and creates an appropriate class to add additional application service logic to.
    • Added IEntityNameManagementService.cs. This app service interface makes unit testing more maintainable because you can mock the service interface and not worry about when the concrete class' constructor arguments change.
    • Added /DataInterfaces/IEntityNameRepository.cs. As described above, every entity now has an explicit repository interface to avoid changes down the road when custom interfaces would inevitably be introduced.
  • /BetterAppServices.Data
    • Added /BetterAppServices.Data/EntityNameRepository.cs to implement the associated interface. An important thing to note is that method provided invokes a stored procedure to act as a reporting means in line with command/query separation. This is useful for entity listing pages which would frequently, previously, end up loading a huge portion of the domain model to show summary information.
    • Added /BetterAppServices.Data/NamedQuery/GetEntityNameSummaries.hbm.xml and set its compile action to "Embedded Resource." This provides the "short cut" for invoking the stored procedure. It could easily be modified to accept paging parameters.
    • Added reference to BetterAppServices.Dtos so that repositories can return DTOs for better command/query separation.
  • /BetterAppServices.Web
    • Added reference to BetterAppServices.Dtos to be able show DTOs on web pages.
    • Changed ComponentRegistrar.AddCustomRepositoriesTo "BetterAppServices.Core" to "BetterAppServices.ApplicationServices" to reflect the new location of the data repository interfaces.
  • /BetterAppServices.Web.Controllers
    • Added reference to BetterAppServices.Dtos.
  • /BetterAppServices.Tests
    • Modified MappingIntegrationTests.CanGenerateDatabaseSchema to save DB schema to /db/Schema/CreateBetterAppServicesDb_ChangesWillBeLost.sql every time the unit test is run.

To reiterate, many of the changes above can be incorporated into a CRUD scaffolding generator; the focus with this example project is on providing an archtectural spike of the proposed architectural revisions.

Even if you don't use S#arp Archtiecture, this sample project should serve as a good example of using application services and basic use of command/query separation (CQS).  Although the CQS in the sample project could be taken much further, I felt that the sample provides a good balance between practical maintainability and a more austere separation of concerns.

Enjoy!

Billy McCafferty


Posted 03-05-2010 11:47 AM by Billy McCafferty

[Advertisement]

Comments

June Hu wrote re: Better Application Services and CQS using S#arp Architecture 1.0 Q3 2009
on 03-05-2010 9:05 PM

Thanks Billy. Can't wait to try it out!

Billy McCafferty wrote A Few Thoughts on DDD, DTOs, View Models, CQS, Repositories and Separation of Concerns
on 03-06-2010 12:09 PM

What I've loved most about developing an open-source project is the ideas that I get from others

Alec Whittington wrote S#arp Architecture in 2010
on 03-07-2010 5:41 PM

So it has been rather quiet on this blog for quite sometime. I could give you the same old excuses that

Chris F wrote re: Better Application Services and CQS using S#arp Architecture 1.0 Q3 2009
on 03-11-2010 5:01 PM

This is fantastic. I've been using S#arp Arch on a corporate back-end system for a year, and have had to do some of these changes myself. However, the CQS stuff with the SPs via NHibernate is pretty awesome, as is the acknowledgment for better DTOs integration.

Great job.

Dan Smith wrote re: Better Application Services and CQS using S#arp Architecture 1.0 Q3 2009
on 05-25-2010 3:41 PM

Hi Billy,

Is there any way to unit test the GetProductSummaries method in the ProductRepository?  When I try to setup a test I get syntax errors with the in memory SQLite database engine.

Thanks

Dan

Billy McCafferty wrote re: Better Application Services and CQS using S#arp Architecture 1.0 Q3 2009
on 06-09-2010 12:32 AM

Dan,

Be sure to check out S#arp 1.5.2 which encapsulates the GetProductSummaries code into HBM files without the need for stored procedures.  Consequently, it has become more easily testable.

Application Services wrote re: Better Application Services and CQS using S#arp Architecture 1.0 Q3 2009
on 12-20-2010 5:40 PM

I'm not sure S#arp will last, it seems so complicated to me.

searslogin wrote re: Better Application Services and CQS using S#arp Architecture 1.0 Q3 2009
on 01-04-2012 9:52 PM

Wooha thanks for the resources!!! just what I needed

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)