Thursday, June 24, 2010

All kinds of Repository implementations

I've been looking back and reviewing the approaches on some of my previous projects. In particular, I've been going back to review a project I did in 2005. At that point, I didn't really have the concepts of domain model and repository clear: for data access I was using POJOs, but loading them internally, within each POJO, via a private method (which called to stored procedures with parameters using SqlDataReader.)

In all projects since, I've used  a Domain Model and Repository, typically with NHibernate for the ORM mapping/queries.

As I started using Rhino Mocks, I also created mock repositories for MVC and MVP unit testing. And I also have created fake repositories (returning collections of dummy objects), and LinqToSql repositories as part of the PRO ASP.NET MVC book examples [Sanderson].

The only combination I hadn't done was original ADO.NET/SqlDataReader in a repository.

In the following code sample, I add an ADO.NET repository to an Model-View-Presenter scenario (which originally had only mocked repository and view interfaces against a Presenter). To make a complete integration test:

1) I create a view interface implementation (WinForm) where the List<Project> setter property assigned its value to a simple data grid
2) I create an AdoNetProjectsRepository implementation class where List<Project> FindAllProjects() method returns a list of Projects from the database.

Integration test with Form1.cs and ADO.NET Repository

[TestFixture]
public class ProjectsPresenterIntegrationTests
{
    [Test]
    public void ShowProjectsEvent_Raised_RetrievesProjectsFromRepositoryAndAssignedToViewProjectsDisplay()
    {
        IProjectsRepository adoNetProjectsRepository = new AdoNetProjectsRepository();
        var projects = adoNetProjectsRepository.FindAllProjects();

        IProjectsView projectsView = new Form1();
        projectsView.ProjectsDisplay.ShouldEqual(projects);
    }
}


IProjectsView implementation as WinForm

public Form1()
{
    InitializeComponent();
    IProjectsRepository adoNetProjectsRepository = new AdoNetProjectsRepository();
    var projectsPresenter = new ProjectsPresenter(adoNetProjectsRepository, this);
    ShowProjects(this, EventArgs.Empty);
}

public event EventHandler ShowProjects;

public List<Project> ProjectsDisplay
{
    get
    {
        return (List<Project>)dgrProjects.DataSource;
    }
    set
    {
        dgrProjects.DataSource = value;
    }
}


Projects Presenter

public class ProjectsPresenter
{
    private readonly IProjectsRepository _projectsRepository;
    private readonly IProjectsView _projectsView;

    public ProjectsPresenter(IProjectsRepository projectsRepository, IProjectsView projectsView)
    {
        _projectsRepository = projectsRepository;
        _projectsView = projectsView;
        _projectsView.ShowProjects += new EventHandler(ProjectsViewShowProjects);
    }

    private void ProjectsViewShowProjects(object sender, EventArgs e)
    {
        _projectsView.ProjectsDisplay = _projectsRepository.FindAllProjects();
    }
}


ADO.NET Repository

public class AdoNetProjectsRepository : IProjectsRepository
{
    public List<Project> FindAllProjects()
    {
        var projects = new List<Project>();

        PopulateProjectsWithSqlDataReader(projects);

        return projects;
    }

    private static void PopulateProjectsWithSqlDataReader(ICollection<Project> projects)
    {
        SqlDataReader sqlDataReader = null;

        try
        {
            var sqlConnection = new SqlConnection(DBManager.DBConnection);
            var sqlCommand = sqlConnection.CreateCommand();

            sqlCommand.CommandText = DBManager.DBOwner + "sp_getCurrentProjects";
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);

            if (sqlDataReader == null) return;

            while (sqlDataReader.Read())
            {
                var myProject = new Project
                {
                    ProjectId = Convert.ToInt32(sqlDataReader["ProjectID"]),
                    Name = sqlDataReader["Name"].ToString(),
                    ProductName = sqlDataReader["ProductName"].ToString()
                };

                projects.Add(myProject);
            }
        }
        finally
        {
            if (sqlDataReader != null) sqlDataReader.Close();
        }
    }
}


The conclusion I drew from this experiment: while I am accustomed to (and may prefer) to use an ORM tool like NHibernate to do my query retrievals in a repository, it is by no means my only option. Even plain old ADO.NET SqlDataReader can do the trick.

The primary goal is not which data access implementation to use, but instead the abstraction of the Domain Model, Repository, and application services (controller/presenter/web service).

Friday, June 18, 2010

A brief overview of Ncqrs

What is CQRS / Ncqrs?
CQRS is an evolutionary step in Domain-Driven Design (DDD). Ncqrs is an open-source implementation for.NET.


Links: CQRS
Wikipedia's entry for Command-query separation.
Udi Dahan's explanation of Command-Query Responsibility Segregation.
Greg Young's video presentation CQRS and Event Sourcing - the Business Perspective.

Links: Ncqrs
For information on Ncqrs, see ncqrs.org.
To try out the Ncqrs walkthrough tutorial, go here.


The goal of this blog post is to compare Ncqrs with regular Domain-Driven Design (particularly when used with Remote Facade), and to examine and understand the processes described in the Ncqrs tutorial.

In regular Domain-Driven Design across a web service boundary, both inputs (commands) and outputs (queries) share the same RemoteFacade and Application Service Layer, as shown in Fig. 1.

Fig.1  Regular DDD with Remote Facade
CQRS
In Command-Query Responsibility Segregation Pattern, inputs (commands) and outputs (queries) are separated and treated very differently. As Greg Young points out in the video presentation (above), using the RemoteFacade/DDD architecture for simple queries leads to an immense amount of busy work (mapping from the domain to DTOs, etc), when the real purpose of the domain model is to implement behavior/business logic--which only occurs based on input (commands).

Therefore:
CQRS in 50 words or less
1) Keep DDD and the domain model, but call it only from commands.
2) Add a new feature: TRACK each command as event history
3) For queries, SKIP the domain model. Do queries quick, dirty and denormalized.

How does Ncqrs implement this approach? Let's walk through the journey that a command follows, based on their tutorial. I've created the following diagram as a guide:

  1. In your backend behind the RemoteFacade, create a custom command class such as PostNewTweetCommand (which inherits from Ncqrs.Commanding.CommandBase).
  2. For any input values you wish to pass to the domain model, create properties in the custom command class. For example:

    public string Message { get; set; }
    public string Who { get; set; }

  3. In your WCF web service, create a method which accepts PostNewTweetCommand as input parameter.
  4. In your client, create the WCF proxy.
  5. In your client, create an action which instantiates PostNewTweetCommand, sets its property values, and passes it to the web service method.
  6. In your WCF web service method, get an ICommandService instance (from Ncqrs.Commanding.ServiceModel) and use it to execute the incoming PostNewTweetCommand input parameter.
  7. Create a PostNewTweetCommandExecutor class (which inherits from CommandExecutorBase<PostNewTweetCommand>) that--by convention--listens for executed instances of PostNewTweetCommand
  8. Have the command executor execute the incoming command, in the context of an IUnitOfWork, against a domain model class (typically an aggregate root)
  9. In the domain model class (eg. Tweet.cs), every public action (such as instantiation, or a method call) creates a domain event to process the incoming command, map over its input values, and then call ApplyEvent().
  10. In this case, the contextual IUnitOfWork writes the event (TweetPostedEvent) to the event store.
  11. As part of setting up the WCF service, three services have been bootstrapped in service of CQRS goals:

    • ICommandService - already mentioned, this processes the incoming commands, and uses convention to call the correct command executor
    • IEventStore - this processes each event transaction and writes it out to a separate data store (in the tutorial example, a SQL Server database)
    • IEventBus - listens for new events, and for each one, makes a call out to the final piece of the ncqrs puzzle (described next)
  12. The event bus alerts classes (again, by convention) which inherit from IDenormalizer where T is the particular event that has been saved.
  13. The implementing class (in this case, TweetIndexItemDenormalizer is so named based on a new, separate database whose purpose is to store, simple, denormalized, read data.
  14. The TweetIndexItemDenormalizer class implements a Handle method, which takes the TweetPostedEvent as input param, and write the values stored in that event to the denormalized data table.
  15. Finally, the client can now (very simply) access data from this denormalized table (eg. as a simple Linq to SQL call to get a list of matching values.)