Domain Model Example Critique

Last post 04-18-2007 3:37 AM by kejroot. 9 replies.

Sort Posts:

  • Domain Model Example Critique

    12-11-2006, 11:31 AM
    • Loading...
    • pseudozero
    • Joined on 12-07-2006, 6:05 PM
    • Posts 10

    Please critique the (fictious and incomplete) code below.  I would like advice on how to make the code into more of a standard architecture or pattern.  The example is a registration system where a person is registering for an event.

    I've been programming for a couple of years now and I've been learning about different techniques for creating an n-layer architecture.  I'm finding some of the concepts difficult without having concrete examples (most of the examples that I have seen on the 'net aren't nearly complete or don't provide answers to problems) so I would like to use the example below as a starting point.

     (Sorry for such a long post.  I'm unable to add an attachment due to permissions.)

    Domain Model Base (Entities)

    1    namespace domainModelExample
    2    {
    3    	/// <summary>
    4    	/// The domain object for a Person
    5    	/// </summary>
    6    	public class Person : domainModelExample.Data.DomainObjectBase
    7    	{
    8    		private string _firstName;
    9    		private string _lastName;
    10   
    11   		public Person() : base() { }
    12   		public Person(string firstName, string lastName)
    13   		{
    14   			this.FirstName = firstName;
    15   			this.LastName = lastName;
    16   		}
    17   		
    18   		public string FirstName
    19   		{
    20   			get
    21   			{
    22   				return this._firstName;
    23   			}
    24   			set
    25   			{
    26   				this._firstName = value;
    27   			}
    28   		}
    29   
    30   		public string LastName
    31   		{
    32   			get
    33   			{
    34   				return this._lastName;
    35   			}
    36   			set
    37   			{
    38   				this._lastName = value;
    39   			}
    40   		}
    41   	}
    42   
    43   	/// <summary>
    44   	/// Represents a registration for an event
    45   	/// </summary>
    46   	public class Registration : domainModelExample.Data.DomainObjectBase
    47   	{
    48   		private Person _person;
    49   		private Event _event;
    50   		private RegistrationType _type;
    51   
    52   		public Registration(Event eventInfo, Person person)
    53   		{
    54   			//TODO: Check to make sure the event and person are not null
    55   
    56   			this.Event = eventInfo;
    57   			this.Person = person;
    58   
    59   			//Adds this registration to event, if not already there
    60   			eventInfo.Registrations.Add(this);
    61   		}
    62   
    63   		public Person Person
    64   		{
    65   			get
    66   			{
    67   				return this._person;
    68   			}
    69   			private set
    70   			{
    71   				this._person = value;
    72   			}
    73   		}
    74   
    75   		public Event Event
    76   		{
    77   			get
    78   			{
    79   				return this._event;
    80   			}
    81   			private set
    82   			{
    83   				this._event = value;
    84   			}
    85   		}
    86   
    87   		/// <summary>
    88   		/// Gets or sets the type of registration
    89   		/// </summary>
    90   		public RegistrationType Type
    91   		{
    92   			get
    93   			{
    94   				return this._type;
    95   			}
    96   			set
    97   			{
    98   				this._type = value;
    99   			}
    100  		}
    101  	}
    102  
    103  	public enum RegistrationType
    104  	{
    105  		Attending,
    106  		NotAttending
    107  	}
    108  
    109  	/// <summary>
    110  	/// The domain object for an Event
    111  	/// </summary>
    112  	public class Event : domainModelExample.Data.DomainObjectBase
    113  	{
    114  		private string _title;
    115  		private RegistrationCollection _registrations;
    116  
    117  		/// <summary>
    118  		/// Gets or sets the title for this event
    119  		/// </summary>
    120  		public string Title
    121  		{
    122  			get
    123  			{
    124  				return this._title;
    125  			}
    126  			set
    127  			{
    128  				this._title = value;
    129  			}
    130  		}
    131  
    132  		/// <summary>
    133  		/// Gets the registrations for this event
    134  		/// </summary>
    135  		public RegistrationCollection Registrations
    136  		{
    137  			get
    138  			{
    139  				if (this._registrations == null)
    140  					this._registrations = new RegistrationCollection();
    141  
    142  				return this._registrations;
    143  			}
    144  		}
    145  	}
    146  
    147  	/// <summary>
    148  	/// A collection of registration
    149  	/// </summary>
    150  	public class RegistrationCollection : System.Collections.Generic.List
    151  	{
    152  		
    153  	}
    154  }
    

    Business Layer (Middle Layer)

    1    namespace domainModelExample.Business
    2    {
    3    	public class PersonManager
    4    	{
    5    		/// <summary>
    6    		/// Saves a person's information
    7    		/// </summary>
    8    		/// <param name="person">The newly created person</param>
    9    		public void SavePerson(Person person)
    10   		{
    11   			//TODO: Check that the person is not null
    12   			//TODO: Check that the person is validated correctly
    13   
    14   			//Get the service provider for the registration domain object
    15   			domainModelExample.Data.IService personService = (domainModelExample.Data.IService)domainModelExample.Data.ServiceFactory.GetService(typeof(Person));
    16   			
    17   			//If new, add the person; otherwise update
    18   			if (person.IsNew)
    19   				personService.Add(person);
    20   			else
    21   				personService.Update(person);
    22   		}
    23   	}
    24   
    25   	public class RegistrationManager
    26   	{
    27   		public void SaveRegistration(Registration registration)
    28   		{
    29   			//TODO: Check that the registration is not null
    30   			//TODO: Check that the registration is validated correctly
    31   
    32   			//save the person
    33   			(new PersonManager()).SavePerson(registration.Person);
    34   
    35   			//TODO: save the event
    36   
    37   			//Get the service provider for the registration domain object
    38   			domainModelExample.Data.IService registrationService = (domainModelExample.Data.IService)domainModelExample.Data.ServiceFactory.GetService(typeof(Registration));
    39   			
    40   			//If the object is new, add it; otherwise update it
    41   			if (registration.IsNew)
    42   				registrationService.Add(registration);
    43   			else
    44   				registrationService.Update(registration);
    45   		}
    46   	}
    47   }
    

    Data Access Layer

    1    namespace domainModelExample.Data
    2    {
    3    	public abstract class DomainObjectBase
    4    	{
    5    		private int _id = DomainObjectBase.EmptyId;
    6    		private bool _isNew = true;
    7    
    8    		/// <summary>
    9    		/// Creates a new domain object
    10   		/// </summary>
    11   		public DomainObjectBase() { }
    12   
    13   		/// <summary>
    14   		/// Creates a domain object that is existing
    15   		/// </summary>
    16   		/// <param name="id">The unique ID of the existing domain object</param>
    17   		public DomainObjectBase(int id)
    18   		{
    19   			//TODO: Make sure the id is of valid value
    20   
    21   			this.Id = id;
    22   			this.IsNew = false;
    23   		}
    24   
    25   		/// <summary>
    26   		/// Gets the unique ID for this object
    27   		/// </summary>
    28   		public int Id
    29   		{
    30   			get
    31   			{
    32   				return this._id;
    33   			}
    34   			private set
    35   			{
    36   				this._id = value;
    37   			}
    38   		}
    39   
    40   		/// <summary>
    41   		/// Gets the value for an empty ID
    42   		/// </summary>
    43   		public static int EmptyId
    44   		{
    45   			get
    46   			{
    47   				return -1;
    48   			}
    49   		}
    50   
    51   		/// <summary>
    52   		/// Gets or sets whether this object is new
    53   		/// </summary>
    54   		internal bool IsNew
    55   		{
    56   			get
    57   			{
    58   				return this._isNew;
    59   			}
    60   			set
    61   			{
    62   				this._isNew = value;
    63   			}
    64   		}
    65   	}
    66   
    67   	/// <summary>
    68   	/// Service interface for a domain object (business entity)
    69   	/// </summary>
    70   	/// <typeparam name="TDObject">The domain object type</typeparam>
    71   	public interface IService
    72   		where TDObject : DomainObjectBase
    73   	{
    74   		void Add(TDObject dobject);
    75   		void Update(TDObject dobject);
    76   		void Delete(TDObject dobject);
    77   	}
    78   
    79   	/// <summary>
    80   	/// Service interface specifically for the person domain object
    81   	/// </summary>
    82   	public interface IPersonService : IService
    83   	{
    84   		
    85   	}
    86   
    87   	/// <summary>
    88   	/// Person domain object service provider for the database
    89   	/// </summary>
    90   	internal class PersonServiceDb : IPersonService
    91   	{
    92   		/// <summary>
    93   		/// Adds a person to the database
    94   		/// </summary>
    95   		/// <param name="person">The person to be added to the database</param>
    96   		public void Add(Person person)
    97   		{
    98   			//Check person domain object isn't null
    99   			//Execute "Insert" stored procedure with person values
    100  			//Get output parameter for the new person's unique ID and save it in the domain object
    101  		}
    102  
    103  		/// <summary>
    104  		/// Updates the person object in the database
    105  		/// </summary>
    106  		/// <param name="person"></param>
    107  		public void Update(Person person)
    108  		{
    109  			//Check person domain object isn't null
    110  			//Execute "Update" stored procedure with person values
    111  		}
    112  
    113  		/// <summary>
    114  		/// Deletes the person object in the database
    115  		/// </summary>
    116  		/// <param name="person"></param>
    117  		public void Delete(Person person)
    118  		{
    119  			//Check to make sure the person isn't null
    120  			//Execute the "Delete" stored procedure
    121  		}
    122  	}
    123  
    124  	/// <summary>
    125  	/// Main interface for other layers to access the services for a domain object
    126  	/// </summary>
    127  	public static class ServiceFactory
    128  	{
    129  		/// <summary>
    130  		/// Gets the service object for the specified domain object type
    131  		/// </summary>
    132  		/// <param name="domainObjectType">The type of domain object to get a service for</param>
    133  		/// <returns></returns>
    134  		public static object GetService(Type domainObjectType)
    135  		{
    136  			if (domainObjectType == typeof(Person))
    137  				return new PersonServiceDb();
    138  
    139  			//Throw a more specific exception - omitted in example
    140  			throw new Exception();
    141  		}
    142  	}
    143  }
    
    Conclusion
    The example above is a fictious example written in under 30 minutes, so its far from complete.
    Filed under: , , , ,
  • Re: Domain Model Example Critique

    12-11-2006, 4:23 PM
    • Loading...
    • evo
    • Joined on 04-28-2005, 1:21 PM
    • USA
    • Posts 168
    Hey - I think you are on track. As long as I don't see a whole bunch of ADO.NET code in your IService implementations (which you omitted) all will be well. This is very similar to the architecture we use on most projects. I'm curious though - how will you code your IService classes --- O/R mapper, codegen, hand code?
  • Re: Domain Model Example Critique

    12-12-2006, 9:31 AM
    • Loading...
    • pseudozero
    • Joined on 12-07-2006, 6:05 PM
    • Posts 10

    For the IService classes, I provided code starting on line 71 of the Data Access Layer section.  I was going to implement the ADO.NET code in the classes that implement IService (like the PersonServiceDb class on line 90).  Is that something I shouldn't be doing?

    I think the word "service" is too ambigious and may have other meanings, but I wanted a way to abstract the datasource (albeit an array of objects or a database) so that during development I didn't have to worry about connecting to a database (and use just a collection instead).  Maybe I'm going about it all wrong.

     As for how I will code the IService classes, all by hand.

  • Re: Domain Model Example Critique

    12-12-2006, 9:44 AM
    • Loading...
    • evo
    • Joined on 04-28-2005, 1:21 PM
    • USA
    • Posts 168
    Yeah I assumed you would be implementing ADO.NET code in the service implementations and it is great to abstract the data source so that you can unit test a bit easier, I have just gotten away from ADO.NET and cringe at the thought of writing out DAO's for all my entities...
  • Re: Domain Model Example Critique

    12-15-2006, 10:47 AM
    • Loading...
    • kejroot
    • Joined on 12-13-2006, 2:01 PM
    • Posts 3
    Here is my opinion about all of this:
    The root namespace contains a classes that implement Fowler's DomainModel domain logic pattern (not just data transfer objects - also may contain validation methods and domain-specific functions). but the DomainObjectBase is this layer's supertype and can't belong to DAL namespace, because domain model shouldn't know about data access layer.
    Why did you call data mapper's layer a business layer? Mappers ("Managers") must belong to data access layer. Each mapper (data source architectural pattern) maps his entity, calling storage helper IService. There should be also a retrieve method in IService (in my similar project i called it an IPersistenceService). This method's argument is hard to build - it can be a TDObject (query-by-example), delegate (but then i don't know how to connect it with UI), a special composite query object (which may be completely pulled out from ORM solution like NHibernate or from an object database like db4o - check the licensing issue) or a plain string (parsing, syntax - it can by XPath or XQuery, and other questions appears). Besides, how to convert a List<DomainObjectBase> to List<Person> or RegistrationCollection and conversely?
    The middle layer consists of Application Controller or Use Case Controllers (discussed here), which can access the UI, DomainModel and DAL for fulfilling the use-case. UI holds copies of DomainObjects binded to controls.
    What about using ORM framework?
  • Re: Domain Model Example Critique

    12-19-2006, 1:00 PM
    • Loading...
    • eccsolutions
    • Joined on 10-31-2005, 3:43 PM
    • Millington, TN
    • Posts 267

    I concur with kejroot on some of this.  The DomainObjectBase does not belong in the DAL.  Your domain should be referenced by your DAL, not the other way around.  As generic as DomainObjectBase is, I would consider creating an "In-House" framework to encapsulate your DomainObjectBase and possibly encapsulate other functionality as a part of DomainObjectBase (i.e. isDirty, generic validation...etc).  This way, if you have multiple solutions in a larger architecture you can reduce redundancies and encourage reuse among the team.  Are you able to use generics?  Is this being done in 2.0?  Might want to consider how generics can help your persistence service become even easier to read and understand.

    Just some thoughts =).  HTH

    Paul
  • Re: Domain Model Example Critique

    12-19-2006, 3:06 PM
    • Loading...
    • pseudozero
    • Joined on 12-07-2006, 6:05 PM
    • Posts 10

    Thanks for the comments!  Here is a list of the proposed changes by kejroot and eccsolutions:

    1. Move "DomainObjectBase" from domainModelExample.Data to domainModelExample (Domain Object Base Layer).
    2. Move "SaveXXX" method(s) from PersonManager and RegistrationManager to be part of the domainModelExample.Data package (Data Layer).
    3. Add methods below to the domainModelExample.Data.IService
          3a. TDObject SelectOne(ISearchCriteria searchCriteria)
          3a. List<TDObject> SelectMany(ISearchCriteria searchCriteria)
      Where ISearchCriteria is an interface to a special composite query object

    When these changes are applied, I will be left with an open business layer, which will be filled with use cases(?).  I'm going to look at the "Use Case Controllers" which were linked to from kejroot's reply.

    I wish I had permissions to upload attachments, because I could share a solution instead of listing the changes.

     Thanks for your replys and critiques!

  • Re: Domain Model Example Critique

    04-15-2007, 1:12 PM
    • Loading...
    • kejroot
    • Joined on 12-13-2006, 2:01 PM
    • Posts 3
    pseudozero:

    I wish I had permissions to upload attachments, because I could share a solution instead of listing the changes.

    Why don't share your solution in cvs on http://sf.net
    http://code.google.com/hosting/
    Microsoft'z http://www.codeplex.com/
    http://freshmeat.net/ ;) or at least could you send it to kejroot#gmail_com, plz?

  • Re: Domain Model Example Critique

    04-15-2007, 6:49 PM
    • Loading...
    • brendan_rice
    • Joined on 02-09-2005, 5:55 AM
    • N.Ireland
    • Posts 141

    I would be interested in seeing the solution as well.  Codeplex would be a good place for it.

    It would be good to have a template to refer people to as this is a common question.

    Thanks

  • Re: Domain Model Example Critique

    04-18-2007, 3:37 AM
    • Loading...
    • kejroot
    • Joined on 12-13-2006, 2:01 PM
    • Posts 3
    here is some idea (not mine) about UI - http://www.thekirschners.com/software/autoui/autoui.html
    Filed under:
Page 1 of 1 (10 items)
Microsoft Communities
Page view counter