Tuesday 22 May 2012

Business Logic Based on Large Data Sets

A collegue recently came to me with help on an architecture desicion: A client wants to add a site to their list of sites, and the system must check that the site does not already exist before adding it, creating a validation error if it does. Now the validation of the new site is clearly something that belongs in the domain, so surely this should be quite straightforward, possibly something like this (this is written off the top of my head, and practically psuedocode):
  
public class Site : Entity
{
    public ValidationMessageCollection ValidateCreateSite(string siteName, string addressLine1, string addressLine2)
    {
        //...

        List<Site> matchingSites = (from matchingSite 
                                    in _sites
                                    where matchingSite.SiteName.Equals(siteName)
                                    && matchingSite.AddressLine1.Equals(addressLine1)
                                    && matchingSite.AddressLine2.Equals(addressLine2)
                                    select matchingSite)
                                    .ToList();

        if (matchingSites.Count() > 0)
            validationMessageCollection.AddError("Similar site already exists.");

        //...
    }
}
OK, ignore the simplistic message, the actual logic behind wheter or not a site is the same as another and whether or not this even compiles, the problem I'm illustrating is based around the fact that a client may have thousands of sites, and to load them all would be wasteful on resourses and slow. Somehow we need the database to do the query, but the domain to do the logic.

Here is my solution - perform the query and then simply pass the result into the domain, and let it make a descision what to do based on the results:
  
public class SiteService
{
    public long AddSite(
        string siteName, 
        string addressLine1, 
        string addressLine2) //Or whatever
    {
        ISiteRepository siteRepository = new SiteRepository();

        bool similarSitesExist = 
            siteRepository.GetWhetherSimilarSitesExist(siteName, addressLine1, addressLine2)

        ValidationMessageCollection validationMessageCollection = 
            Site.ValidateCreateSite(siteName, addressLine1, addressLine2, similarSitesExist)

        if(validationMessageCollection.Count > 0) 
            throw new ValidationFaultException(validationMessageCollection);

        Site site = Site.Create(siteName, addressLine1, addressLine2);
        siteRepository.SaveInTransaction(site);
        return site.Id.Value;
    }
}

public class Site : Entity
{
    public ValidationMessageCollection ValidateCreate(
        string siteName, 
        string addressLine1, 
        string addressLine2, 
        bool similarSitesExist)
    {
        //...

        if (similarSitesExist)
            validationMessageCollection.AddError("Similar site already exists.");

        //...
    }
}
Or, if you want to return more information about the sites that exists, this could be an option:
  
public class SiteService
{
    public long AddSite(string siteName, string addressLine1, string addressLine2) //Or whatever
    {
        ISiteRepository siteRepository = new SiteRepository();

        List<Site> similarSites = 
            siteRepository.GetSimilarSites(siteName, addressLine1, addressLine2)

        ValidationMessageCollection validationMessageCollection = 
            Site.ValidateCreateSite(siteName, addressLine1, addressLine2, similarSiteAddresses)

        if(validationMessageCollection.Count > 0) 
            throw new ValidationFaultException(validationMessageCollection);

        Site site = Site.Create(siteName, addressLine1, addressLine2);
        siteRepository.SaveInTransaction(site);
        return siteAddress.Id.Value;
    }
}

public class Site : Entity
{
    public ValidationMessageCollection ValidateCreateSite(
        string siteName, 
        string addressLine1, 
        string addressLine2, 
        List<SiteAddress>> similarSites)
    {
        //...
              
        if(similarSites.Count() > 0)
        {
            string similarSiteAddressString = "Similar site addresses exist: ";
                     
            foreach(SiteAddress similarSiteAddress in similarSiteAddresses)
            {
                string = string + similarSiteAddress.Name + ", " + similarSiteAddress.AddressLine1;
            }
                     
            validationMessageCollection.AddError(similarSiteAddressString);
       }
              
       //...
    }
}

1 comment:

  1. One of the most useful things about keeping this blog is that it allows me to review decisions I made and thoughts I had in the past. Reading this now one thing instantly jumps out at me: validation of the new site is NOT clearly something that belongs in the domain. This could be done as a query directly from the UI.

    ReplyDelete