Over the weekend, I spent some time refactoring some business rules in my current project, to use the ISpecification<t> framework that I previously created. One of my coworkers had asked about logical groupings - making sure that we can handle complex logic such as, "(this and this) or this". Since my project actually made use of logical groupings I wanted to share with the world and show how easy it is. In this example, I have a Date Range that is being selected – a From date and a To date. The rules are as follows for validating the range: - If a From date and a To date are provided, the From date must be equal to, or before, the To date
- Or, you can specify a From date without To date
- Or, you can specify a To date without From date
The following is the actual code that I’m using to define these business rules, via the ISpecification<t> framework. PredicateSpec<ExtractSearchCriteria> fromDateProvided = new PredicateSpec<ExtractSearchCriteria>(crit => crit.FromDate > DateTime.MinValue); PredicateSpec<ExtractSearchCriteria> toDateProvided = new PredicateSpec<ExtractSearchCriteria>(crit => crit.ToDate > DateTime.MinValue); PredicateSpec<ExtractSearchCriteria> fromDateBeforeToDate = new PredicateSpec<ExtractSearchCriteria>(crit => crit.FromDate <= crit.ToDate); ISpecification<ExtractSearchCriteria> validDateRangeSpec = ( (fromDateProvided.And(toDateProvided)) .And(fromDateBeforeToDate) ) .Or( fromDateProvided.Not(toDateProvided) ) .Or( toDateProvided.Not(fromDateProvided) );
Here, I am defining the core of the business rules – to check if the From date was provided, to check if the To date was provided, and to check if the From date is before or equal to the To date. From these core specifications, I can build the aggregate spec that handles all of the stated rules for validating the date range.
There are three logical groupings using parenthesis to represent each of the business rules that can comprise a valid date range. The first logical grouping checks to see if both a From and To date are provided, and checks if the From date is before or equal to the To date, if both are provided.
( (fromDateProvided.And(toDateProvided)) .And(fromDateBeforeToDate) )
The second group is specified with an Or, and checks to see if we provided a From date but not a To date.
.Or( fromDateProvided.Not(toDateProvided) )
And the third group also uses an Or, and checks to see if we provided a To date but not a From date.
.Or( toDateProvided.Not(fromDateProvided) )
The end result is that we have one of three possible ways for this date range to be valid – all logically grouped and relatively easy to read (compared to a bunch of if-then statements, at least). Even better – we re-used two out of three simple ISpecification<t> objects to create all of the rules for this validation check. This helps to create a validation that is not only easier to read, but easier to maintain and enhance in the future.
My Previous Post talked about a sample Specification pattern implementation. Today, during a discussion with some team members, I had the revelation of how fluent interfaces are really built and how they operate under Closure of Operations. So I decided to expand my previous sample code with a more operators on the spec class, and I came up with a very powerful fluent interface for creating specifications. Here is the example I coded into my spike, to illustrate a fluent interface. Note that I changed nothing in my implementation of the ISpecification<t> or base Specification<t> from yesterday - other than to add the "Or" and "Not" specifications. The fluent interface was already there and available! //show the real power of Closure Of Operations, in creating a fluent interface! ISpecification<Foo> chainedSpec = equalSpec .And(passingSpec.And(trueSpec)) .Or(passingNotSpec) .Not(failingSpec.Or(falseSpec)) .And ( passingSpec.Or ( failingSpec.Not(falseSpec) ) );
I'll leave it to you, the reader, to implement the "Or" and "Not" specification classes. It should be pretty simple, based on the code I posted yesterday.
I can see how we could easily use an a "Closed" interface like this, to create a more fluent implementation of business rules and logic. I've often implemented multiple if-then statements in order to evaluate equality and equivalence, via multiple methods and a lot of ugly code. This specification implementation may be the answer to that ugliness.
And suddenly, Ayende's UnitOfWork and NHibernate's ICriteria make a lot more sense to me... I love those "AHA!" moments. 
Inspired by my recent readings in Domain Driven Design - specifically Chapter 10, "Supple Design" - and recent posts by David Laribee and Nigel Sampson, in combination with the recent pains I've been putting myself through, trying to test query generation code in a search screen, I decided to spike out a quick example of a reusable Specification implementation. Rather than repeat what's already been said, I'm just going to get straight to the code. using System; namespace Spec_Spike { class Program { static void Main() { Foo foo1 = new Foo(); foo1.Bar = "Test"; ISpecification<Foo> equalSpec = new Specification<Foo>(foo => foo.Bar == "Test"); ISpecification<Foo> notEqualSpec = new Specification<Foo>(foo => foo.Bar != "Not Equal To This Text"); ISpecification<Foo> falseSpec = new Specification<Foo>(foo => false); ISpecification<Foo> passingSpec = equalSpec.And(notEqualSpec); ISpecification<Foo> failingSpec = passingSpec.And(falseSpec); Console.WriteLine(equalSpec.IsSatisfiedBy(foo1)); Console.WriteLine(notEqualSpec.IsSatisfiedBy(foo1)); Console.WriteLine(passingSpec.IsSatisfiedBy(foo1)); Console.WriteLine(failingSpec.IsSatisfiedBy(foo1)); } } public class Foo { public string Bar; } public interface ISpecification<t> { bool IsSatisfiedBy(t obj); ISpecification<t> And(ISpecification<t> lhs); } public class Specification<t>: ISpecification<t> { private readonly Predicate<t> _pred; public Specification(Predicate<t> pred) { _pred = pred; } protected Specification(){} public virtual bool IsSatisfiedBy(t obj) { return _pred(obj); } public ISpecification<t> And(ISpecification<t> andSpec) { return new AndSpecification<t>(this, andSpec); } } public class AndSpecification<t>: Specification<t> { private readonly ISpecification<t> _spec1; private readonly ISpecification<t> _spec; public AndSpecification(ISpecification<t> spec1, ISpecification<t> spec) { _spec1 = spec1; _spec = spec; } public override bool IsSatisfiedBy(t obj) { return (_spec.IsSatisfiedBy(obj) && _spec1.IsSatisfiedBy(obj)); } } }
Drop this code into a console app in C# 3.5 and watch the magic happen. Here's the output:
These are the results that I expected - the first 2 individual specs passed, the first combined spec passed, and the last combined spec failed.
Overall, I'm fairly excited about the possibilities here. I'm thinking that I may actually be able to properly unit test the query generating code in my search screen with this basic technique. I'm still not 100% sure on that, but I plan on trying, anyway.
For more information on the Specification pattern, I highly recommend you read the previously linked posts by David Laribee and Nigel Sampson, in addition to reading all of the Domain Driven Design Book. This is one of those books that should fundamentally change the way you think about software development.
I'm currently working on a search screen for a shipment tracking system. At the bottom of this screen, there are 4 checkboxes that will determine whether or not we are supposed to display Imports, Exports, Air shipments, or Ocean shipments - in whatever combination the user wants: Down in the depths of the search process, I am creating a "finder" object, as Ayende talked about quite a while back. In this object, I have to account for the checkbox values here - whether or not the user wants to display whatever types of shipment (import or export) and/or the method of shipment (air or ocean). What gets really interesting is that when a box is un-checked, I should not show that particular type or method. A quick analysis of these 4 checkboxes will come up with the following variants that must be accounted for when building the query. - Show Imports Only
- Ocean and Air
- Ocean only
- Air Only
- Show Exports Only
- Ocean and Air
- Ocean only
- Air only
- Show Imports and Export
- Ocean and Air
- Ocean only
- Air only
- Show no imports or exports (returns no results)
- Show no ocean or air (returns no results)
All totaled up, that's 11 different query variants that have to be accounted for. The end result of my query build methods is the following: private void AddShipmentMethodCriteria(DetachedCriteria criteria) { ICriterion air = Restrictions.Eq("ShipmentMethod", "Air"); ICriterion ocean = Restrictions.Eq("ShipmentMethod", "Ocean"); if (searchCriteria.ViewAirShipments && searchCriteria.ViewOceanShipments) criteria.Add(Restrictions.Or(air, ocean)); else { if (searchCriteria.ViewAirShipments) criteria.Add(air); else criteria.Add(Restrictions.Not(air)); if (searchCriteria.ViewOceanShipments) criteria.Add(ocean); else criteria.Add(Restrictions.Not(ocean)); } } private void AddShipmentTypeCriteria(DetachedCriteria criteria) { ICriterion import = Restrictions.Eq("ShipmentType", "Import"); ICriterion export = Restrictions.Eq("ShipmentType", "Export"); if (searchCriteria.ViewImports && searchCriteria.ViewExports) criteria.Add(Restrictions.Or(import, export)); else { if (searchCriteria.ViewImports) criteria.Add(import); else criteria.Add(Restrictions.Not(import)); if (searchCriteria.ViewExports) criteria.Add(export); else criteria.Add(Restrictions.Not(export)); } }
By having the first If statement check for both 'Import' and 'Export' being requested, we can properly create our query to show both of them via the Restrictions.Or() criteria. Additionally, we have to account for either or both of them not being checked and explicitly call them out to say that we do not want to show whichever one is not selected. The same is true for the 'Air' and 'Ocean' shipment methods.
The end result is that the user can select or un-select whichever shipment methods and shipment types they want, resulting in the correct data being displayed.
...
On a side note, there's probably some abstraction that I could create where I pass in the ICriterions and the boolean flags to help reduce code redundancy but that's not the point of this post. I'm really trying to illustrate the complex logic used in creating the correct NHibernate ICriteria/DetachedCriteria, and the analysis that you need to undertake for what looks like the most simple of situations. After all, how hard could it be to create a query based on 4 check boxes? ... more difficult than you might imagine, at first. Take the time to analyze even simple scenarios like this.
Yesterday, I posted a quick thought on code generation, and one of the statements I made is worth re-stating to stand on it's own. In Agile/Lean software development, a single User Story is one piece flow when implemented via TDD, in a Workcell In Lean Manufacturing, one piece flow is the idea that you do not produce anything in batches, but that you produce one product from start to finish per a customer's order. In Lean / Agile software development, this is analogous to an iteration with user stories. An iteration backlog is a customer order - a set of behaviors or features that are requested to be delivered in the current iteration. A user story is the single piece that we want to flow from beginning to end - from the start of coding all the way to acceptance via user testing. In manufacturing, a Workcell is used to facilitate one piece flow through the manufacturing process. In software development, a Workcell is also used to facilitate the one piece flow. The Workcell may be 1 person, pair programming or a three person team. In order for it to truly be one piece flow, though, strict standards such as TDD and automated acceptance tests must be followed. I'm not going to detail the benefits of one piece flow, here (not yet, anyway... another post for another time). I highly recommend that you read The Toyota Way and Lean Thinking for a good understanding of this concept and the benefits it provides.
Until around a year ago, I was an advocate of code generation via CodeSmith. Having marginal "success" with it in the 4 years I advocated for it, I'm now of a different opinion. Code generators, such as CodeSmith, are automated overproduction machines that require prior overproduction, in the form of schema, to be used Micro code generators like as Resharper, are much closer to JIT machines when lined up in one-piece-flow processes, such as Test Driven Development ... If you had told me, 2 years ago, that I would make these statements today, I probably would have laughed at you. 
A coworker asked this question about our automated integration test suite, recently: "with the RowTest feature of NUnit, does the test data being used need to be hard coded into my code or can it be a variable that gets defined based on some rule?" Here is my response, outlining the need to know the state of the database (know every value of every field, in every table) before and after the test suite is run: The short answer is that the tests will have data hard coded into them. This may be a change from how you have looked at integration testing, previously – where the data is changed on each run, so as not to duplicate the data in the system. Rather than changing the data that is used during each test, we need to ensure that the data is exactly what we expect, before and after each test. If the data is not what we expect before the test is run, the test cannot produce the data that we expect to have after it is run. If the data is not what we expect after a test is run, the test fails. At a very high level, automated tests against the database require three things: - A known, state of ALL data in the database
- A known, suite of unit tests that manipulate said data in predictable ways
- An expected, verifiable suite of data returned by the software and manipulated in the database
The data is not necessarily "perfect" in that it has no flaws in it – rather, every last character in every last field of every last table is known, and all of the ramifications of that data is known with the explicit purpose of that data being there to support the automated tests. The automated tests expect this "perfect" data to produce the desired results and verify that the system works as expected. Any data that is manipulated in the system is expected to be in a predictable state, so the tests can verify that the system manipulated the data correctly. When it comes to implementation of this, there are some fairly strict requirements for it to work: - Before the first test is run from the test suite, the data must be exactly what the tests expect
- After the tests have run, the data must be exactly what the tests expect
- Repeat for each run of the test suite
What this really means is that we cannot add or modify data between two test runs, without changing the tests that work with the data. In order for the tests to run multiple times throughout the day and/or on-demand, we have to ensure that the data being manipulated is what we expect it to be. Therefore, the first step of any run of the test suite is to revert the database to the known state, through sql scripts or code that are automatically run before the tests are run.
Here's the format that my team is currently using for User Stories and Acceptance Criteria. These formats are primarily learned from Scott Bellware's training that we have had recently, but is also influenced by the actual project that we are currently working. User Stories As a [Role], I want to [Goal], so that [Motivation]. Role: Who is using the system Goal: What you want to do with the system Motivation: Why you want to use the system. I typically see people asking questions about Motivation - why it's important, etc. It's important because it gives the implementer a frame of mind for the behaviour. Example: As a Nurse, I want to record patient pain levels, so that I can adjust their medication doses appropriately. vs. As a Nurse, I want to record patient pain levels, so that I can show the pain trends with certain types of medication over a period of time. When I read these two stories, as a developer, I begin to see drastic requirements differences and expect certain Acceptance Criteria based on the requirements differences that I expect to see. If I'm only adjusting their medication level, I probably only want to see what their current pain level is and whether or not I can adjust the level within dosage limitations. If I'm monitoring trends over a long period of time, I will want to record more - dose for period of time, when adjusted and why for the pain level, what other medications were involved in that patient in the same time periods, etc. The motivation can cause a huge difference in how the story is implemented. Acceptance Criteria I've noticed that there are two types of Acceptance Criteria: Behavioral (functional) and technical (non-functional). As such, there may be two different formats for a story. Functional Acceptance Criteria When [verb], should [verb], should ... Example: When recording the patient pain level Should represent the level on a scale of 1 to 10 where 1 is no pain and 10 is extreme pain Non-Functional Acceptance Criteria Should [technical detail] or When [functional criteria], Should [technical detail] Example: The pain level recording screen must be usable via a touch screen tablet pc. or When recording patient levels on a tablet pc, the data should be cached locally and synchronized to the master database when the nurse docs the tablet pc. I believe the functional acceptance criteria should be stated in technology and implementation agnostics terms - imagine for a moment that you are going to implement the requirements via building blocks, paper forms, or printed circuit logic. If you have to change the functional acceptance criteria based on the implementation technology, then the criteria is not formatted correctly in the first place. Avoid joining functional and non-functional acceptance criteria into a single acceptance criteria. It is easy for a developer to lose sight of the behavioral goals when these are mixed, and may prevent a UI/UX specialist from creating the greatest workflow ever.
Thanks to everyone who put together and presented at Austin Code Camp '08, on Saturday. It was a ton of fun. I learned a lot (especially in Ben Scheirman's Resharper-Fu session), met a lot of great people (the 3 LostTechies that presented, plus many more - including some that traveled all the way from Arkansas!), and participated heavily in Chad's Fishbowl TDD/BDD Discussion session. You can see what classes I attended, over at my Twitter account - in the few days I've been on Twitter, i've become an addict. The gathering after was also great fun. I'd never been to NxNW before. Good food, good drinks (micro brew), excellent conversation. I met some more great people, including Gordon Montgomery - he's doing some really cool stuff with User Experience and did a session @ACC that I wasn't able to make it to. But best of all, I got to see what Scott Bellware will look like in 20 years (sorry for the lousy picture quality - taken from my cell phone). No, not the lady in the skirt... the guy with the hat and glasses. ... Looking forward to Austin Code Camp '09!
I've been toying around with various ideas in code, and I've come to the conclusion that the following formula is true: Lambda Expressions + Func<> and Action<> = Easy Command Patterns As a very contrived, quick example: public class DoStuff { private Func<CommandResult> Step1 { get; set; } private Func<CommandResult> Step2 { get; set; } public DoStuff(Func<CommandResult> step1, Func<CommandResult> step2) { Step1 = step1; Step2 = step2; } public void DoTheStuffProcessing() { CommandResult result = step1(); if (result.Succeeded) { step2(); } } }
It's ... so ... beautiful ... 
|