zaterdag 31 mei 2008

Building an Allors application

As explained in my previous post I am going to build an application to demonstrate how to use Allors.  I will hopefully have to time to build the same application later one using NHibernate to show both frameworks differences. 

The application I'll be building will take care of the invoicing for a contractor.  It is built using the YAGNI principle (this basically states if you think you need to add complexity to your domain model, you should defer it until the moment you actually need it.  Probably you ain't gonna need it).  Lets discuss the domain shall we.  The most important class obviously is the Contract signed between a Customer and a contractors's Company.  Both the customer and the company are subclasses of the corporation class, which gives them some common attributes (Name, Address and Account).  On the Contract you can register your hours you worked for them and eventually generate an Invoice for a specific period.

All relations and classes are shown in the following model:

Version1 
I deliberately kept everything as simple as possible so I can make future improvements to demonstrate what this means in terms of database upgrades, refactorings possibilities, etc....  If you want to follow everything that is explained here below, you can download the complete solution here or built everything yourself of course :-)

Building the domain (Allors)

To start building an Allors domain you first need to download the binaries and the Allors Repository.  You can either checkout the sources from the subversion (https://www.allors.com/svn/platform) or you can download the Quick Start file I've put together. The zip contains two directories & a command file

  • lib: Directory containing the Allors binaries and the Allors Repository, you should extract these in your solution folder.
  • Allors: Directory for the Allors Domain Files, you should extract these in your domain project folder.
  • AllorsRepository.cmd: file that will startup the AllorsRepository Tool for you domain, this should be extracted in your solution folder.

Step 1: Setting up the solution hierarchy

The solution structure I have applied consists of 4 projects.

  • Diagrams: containing the diagrams of my domain.  This project will contain only generated code with getters/setter that reflect your domain.  You can then use this generate your class diagrams.
  • Domain: the heart and soul of our accounting application, you should refer to the Allors.Framework.dll here.
  • Population: Helper classes to build your domain objects and setup an initial population for unit/integration testing.
  • Unittests: proving our design and domain is correct and well thought.

image 

Step 2: Creating the Allorized domain

The Allors folder from the zip file needs to be copied into your domain folder.  This folder will contain the meta information (allors.repository) about your domain classes, which is built up using the AllorsRepository-Application (included in the zip file).  if you modify the command file that was included in the zip file so that the parameter passed in to start up the "Allors.Repository.Application.exe" refers to the allors.repository in your domain folder, this file will now fire up the AllorsRepository-Application for your domain.  You should see the following (if you are building from scratch, otherwise your domain will be completely filled in).

image

This screen has three main parts. 

  • The top left has a tree containing all your types and relations in your domain
  • The top right is the property window of the selected treeNode
  • The bottom gives information about errors in your domain

All actions are available by rightclicking the treeNode and then select the action in the context menu or change the node's corresponding properties.  If you need to know how things are getting done, you can checkout the getting started page of the Allors website.  On the bottom you can see that the repository already has one error.  It has no name, so you need to select the domain node and fill in the name in the properties window.

Step 3: Namespaces & Types

Our solution will have two namespaces to start with.  You can see here how you can add namespaces in the repository.  The types we are building are shown in the diagram above, you can easily add them yourself in the allors repository.  It is written out here how you can achieve that.  All objects belong to the Accounting namespace, only the DatePeriod type is more generic and is placed into its own General namespaces

Step 4: Attributes & Relations

We now have domain containing single classes without any attributes or relationships between them.  You cannot call this a domain of course so the logical next step to give our objects data and connect them through the use of relations.  As always you can checkout here how everything can be done.  The above diagram gives you all the information you need to create all the relations.

Step 5: Adding Domain Logic

The Allors domain has been built (you can download the zip file here).  The next step is to generate (right click the domain and select generate) our allors classes, these will be our base classes for our domain objects.  You don't have to remember to inherit any classes or interfaces because we generate a partial class for you as well.  Include these files (located inside the Allors/output/folder by default, but this can be tweaked of course) in your domain project and you can start using them.  Every object you instantiate now is done inside a Session, the creation is performed through the Session as you can see in the next code block:

public static Account Create(AllorsSession session,
string bank,
string number)
{
Check.Argument(number,
"number").IsNotNullAndNotEmptyAndNotWhiteSpace();
Check.Argument(bank,
"bank").IsNotNullAndNotEmptyAndNotWhiteSpace();

var account
= session.Create<Account>();
account.Bank
= bank;
account.Number
= number;

return account;
}


Adding the actual business logic is done simply by adding the methods into your class (as you normally would)



public TimeRegistration RegisterWorkingTime(DateTime date, Double hours)
{
return TimeRegistration.Create(AllorsSession, date, this, hours);
}

public Invoice BuildInvoice(DatePeriod period,
String reference,
DateTime invoiceDate,
IInvoiceCalculator invoiceCalculator)
{
VerifyThatInvoiceReferenceIsUnique(reference);

var timeRegistrations
= new TimeRegistrationFinder(AllorsSession)
.GetNotInvoicedTimeRegistrationsFor(
this,period);
if (timeRegistrations.Length == 0)
{
throw new ArgumentException("Invoice must contain TimeRegistrations");
}

var invoice
= Invoice.Create(AllorsSession, reference, invoiceDate);
AddInvoice(invoice);

foreach (var timeRegistration in timeRegistrations)
{
invoice.InvoiceTimeRegistration(timeRegistration, invoiceCalculator);
}

return invoice;
}


Check the complete sample along with the unittests for a complete overview.  As you notice, the major difference with traditional POCO domain objects is that inheritance from an Allors Class.  This Class encapsulates the access to a strategy object which contains all your class attributes.  This way we can keep all objects managed by Allors which has a lot of benefits, but that is for a next post.



Let me know what you think!



 







Submit this story to DotNetKicks

Submit this story to DotNetKicks

Geen opmerkingen: