Thursday, December 20, 2018

Domain Model Pattern

Business Logic that involves One Row in a Single Database Table. 
For Collection of Rows, we have Domain Services. 

Business Logic means some Computations or Calculations. If it is some Filtering or Records, it should be done in the Data Access Layer rather than in Domain Layer or App. Service layer.

About Domain Model

All Martin Fowler Text Related to Domain Model is Applicable only to Entities. it is the DDD that discovers the further Patterns within the Domain Model like Entities and its Variations, VO and it variations, Domain Events and it variations, Factories and it variations,

In Domain Model, Order Objects Corresponds to One Order. Business Methods work on just one Order. Order Class encapsulate the Domain Logic related to one Order.

Thinking is that, Order object is loaded based on the ID. All Business Methods related to a single Order are housed inside the Order Class.

Domain Logic that involves Collection of Orders goes into the Domain Services Class.

One of the key messages of object orientation is bundling the data with the behavior that uses it. 

The traditional object-oriented approach is based on objects with identity, along the lines of Domain Model (116). 

Thus, if we have an Employee class, any instance of it corresponds to a particular employee.

This scheme works well because once we have a reference to an employee, we can execute operations, follow relationships, and gather data on him.

It is an OO API that validates the Business Rules against single Domain Object. Domain Logic that involves Collection of Same Objects or Involve more than one Domain Objects goes to the Domain Services.

One of the problems with Domain Model (116) is the interface with relational databases. 

In many ways this approach treats the relational database like a crazy aunt who's shut up in an attic and whom nobody wants to talk about. Means Object Relations Impedance Mismatch. OO API is hard to wrap around the RDBMS. As a result you often need considerable programmatic gymnastics to pull data in and out of the database, transforming between two different representations of the data. All the Hard Things of ORM are done by the ORM Frameworks.




The short definition for the Domain Model Pattern is: “an object model of the domain that incorporates both behavior and data”.
The domain model is created in an application by introducing a whole layer of objects which model the business concepts and entities that the application is working in. It creates a set of interconnected objects for dealing with the logic required by the business environment. An object-oriented domain model will be quite like the database model it uses, but domain model not only contains the data and also the process need to be used with the data.
There are two common ways of using domain model. One is to model each database table (or logic tables based on application requirement) as one class. Other design patterns and inheritance can also be used here. Another way is to introduce rich domain model which is good for complex domain logic, and as the result, the model will not look like the data model anymore. The Active record pattern can be used as the data mapper if the domain logic is not very complex; in the case of very complex domain logic, the Data mapper pattern should be used to map between the application and the data.

Organizing Domain Logic: Domain Model


The third pattern for representing domain logic in an application is the Domain Model. In this model the application is viewed as a set of interrelated objects. The core feature of these objects (referred to as Business Entities in Microsoft nomenclature) is that, unlike the Table Module approach, each object maps to an entity (not necessarily a database table) in the database. In other words, each primary object represents a single record in the database rather than a set of records and the object couples the object’s data with its behavior (business logic and data validation). Additional objects may represent calculations or other behavior. Ultimately, this approach requires at least the following:
  • Object-relational Mapping. Since this pattern does not rely on the DataSet object or even Typed DataSet objects, a layer of mapping code is required. At present the Framework does not contain built-in support for this layer. Look for support in the Microsoft Business Framework (MBF) technology to be released after the Whidbey release of VS .NET. Techniques to perform this mapping are covered in my article Software Design: Using data source architectural patterns in your .NET applications.
  • ID Generation. Since each Domain Model object maps to an entity in the database, the objects need to store the database identity within the object (the CLR’s object system uniquely identifies each object based on its data). There are several techniques to do so documented by Fowler in his discussion of the Identity Field pattern.
  • Strongly-Typed Collection Classes. Because objects in the Domain Model map intrinsically to an individual entity, representing multiple objects is handled through collections. Framework developers can therefore create strongly-typed collection classes to handle representing multiple domain objects. For more information see my article Take advantage of strongly typed collection classes in .NET.
The end result of a domain model is that the application manages many individual and interrelated objects (using aggregation) during a user’s session. Although this may seem wasteful, the CLR’s efficiency at managing objects makes the Domain Model a valid approach. Such an approach would not have been efficient in the world of COM, however. The Domain Model can also take advantage of inheritance by implementing a Layer Supertype.
To build an object used in a Domain Model a best practice is to follow the Layer SuperType pattern. Using this pattern an architect would develop a base class from which all domain objects would inherit as shown here:
<Serializable()> _

Public MustInherit Class BusinessObjectBase : Implements IComparable 

    Protected _id As Integer ' could also use GUID or a key class

    <xml.serialization.xmlattribute()> _

    Public Property Id() As Integer

        Get

            Return _id

        End Get

        Set(ByVal Value As Integer)

            _id = value

        End Set

    End Property

   MustOverride Function Save() As Boolean

   MustOverride Function Delete() As Boolean

   Public Shadows Function Equals(ByVal o As BusinessObjectBase) As Boolean

        If Me.Id = o.Id Then

            Return True

        Else

            Return False

        End If

   End Function

   Public Shared Shadows Function Equals(ByVal o As BusinessObjectBase, _

       ByVal o1 As BusinessObjectBase) As Boolean

        If o.Id = o1.Id Then

            Return True

        Else

            Return False

        End If

   End Function

   Protected IsDirty As Boolean = False

   ' Used when the Sort method of the collection is called

   Public Function CompareTo(ByVal o As Object) As Integer _

     Implements IComparable.CompareTo

        Dim b As BusinessObjectBase = CType(o, BusinessObjectBase)

        Return Me.Id.CompareTo(b.Id)

   End Function

End Class

You’ll notice that this abstract class contains the implementation of the Id property as well as the abstract Save and Delete methods and a protected field to determine if the object has been altered and is in need of removal. In addition, it shadows (implemented with the new keyword in C#) both signatures of the Equals method inherited from System.Object that is used to determine whether two instances of the object are equal. Note that these methods check the Id property but could alternately have been implemented to check against some other criterion or more likely overridden in the derived class and checked against other properties of the object. This class also uses the SerializableAttribute and XmlAttribute classes so that the object can be serialized to XML and its Id property represented as an attribute.

A derived Domain Model object, implemented by the Order class might then look as follows.
<serializable> Public Class Order : Inherits BusinessObjectBase

    Private _orderId As Long

    Private _cust As Customer

    Private _prod As Product

    Private _quant As Integer = 1 'default

    Private _ship As ShipType = ShipType.Postal 'default

    Public Sub New()

    End Sub   

    Public Sub New(ByVal cust As Customer, ByVal prod As Product)

        _InitClass(cust, prod, Nothing, Nothing

    End Sub

    Public Sub New(ByVal cust As Customer, _

      ByVal prod As Product, ByVal quantity As Integer)

        _InitClass(cust, prod, quantity, Nothing)

    End Sub

    Public Sub New(ByVal cust As Customer, ByVal prod As Product, _

     ByVal quantity As Integer, ByVal ship As ShipType)

        _InitClass(cust, prod, quantity, ship)

    End Sub

    Private Sub _InitClass(ByVal cust As Customer, _

     ByVal prod As Product, ByVal quantity As Integer, _

     ByVal ship As ShipType)

        _cust = cust

        _prod = prod

        Me.Quantity = quantity

        Me.ShipVia = ship

        Me.IsDirty = True

        ' Generate a new or temporary order id: use a system assigned key

        ' _orderId = key table  GUID

    End Sub

    Public ReadOnly Property Customer() As Customer

        Get

            Return _cust

        End Get

    End Property



    Public ReadOnly Property Product() As Product

        Get

            Return _prod

        End Get

    End Property



    Public Property Quantity() As Integer

        Get

            Return _quant

        End Get

        Set(ByVal Value As Integer)

            If Value < 0 Then

                Throw New ArgumentOutOfRangeException( _

                 "Quantity must be greater than 0")

            End If

            _quant = Value

           Me.IsDirty = True

        End Set

    End Property



    Public Property ShipVia() As ShipType

        Get

            Return _ship

        End Get

        Set(ByVal Value As ShipType)

            _ship = Value

            Me.IsDirty = True

        End Set

    End Property



    Public Function CalcShippingCost() As Double

        ' calculate the shipping cost based on the Customer and Product objects

        ' store the shipping cost for this order

    End Function



    Public Function CalcOrderTotal() As Double

        ' calculate the total cost of the order with tax

        ' store the cost for this order

    End Function



    Public Function IsComplete() As Boolean

        ' Determines whether this order has enough information to save

    End Function



    Public Overrides Function Save() As Boolean

        ' Persist the order

        Me.IsDirty = False

    End Function



    Public Overrides Function Delete() As Boolean

        ' Remove the order

    End Function

End Class

The key point to note about this class is that it combines data (the Quantity and ShipVia properties), behavior (the IsComplete, Save, CalcOrderTotal and other methods), and the relationships to other data (the Product and Customer properties that relate to the Product and Customer classes). The end result is a static structure diagram:

The second key point to note about this structure is that each object derived from BusinessObjectBase creates its own Id property in the object’s constructor and the Id property is represented as a Long (64-bit) integer. The strategy shown here is only one among several documented by Fowler as the Identity Field pattern. The considerations for Id generation are as follows:
  • Use of system assigned keys. Here the assumption is made that the key value will be assigned by the system and not use a “natural key”. In practice this proves to be the more durable design since the independence of the keys allows them to remain fixed once assigned. This also disallows the use of compound keys (which if used should be implemented in a separate key class) which are difficult to manage and slow performance of the database.
  • Use of data types. Here a 32-bit integer is used, however, unless uniqueness can be guaranteed in the generation of the ids it might be possible to duplicate keys which would cause problems once the object is persisted to the database. An alternative would include the use of System.GUID, which is (for all intents and purposes) guaranteed to be unique. If you wish to protect yourself against data type changes in the key you could also implement the key in a key class that abstracts the data within the key and performs any comparisons and generation.
  • Generation of the ids. If a data type such as an integer is used, one technique for generating unique ids is to generate them from a key table. The downside of this technique is that it incurs a round trip to the database server.
  • Uniqueness of the ids. In the code shown here it is assumed that each object generates its own key probably through the use of a key table. This then assumes that each object type (Order, Customer, Product) generates keys independently of the others. An alternate approach would be to create system wide unique ids through a key table, in which case the generation of the id could be placed in the base class. If the classes form an inheritance relationship, for example if the BookProduct class inherited from the Product class it would be assumed that the generation of the ids would take place in the Product class so that all of the objects generated by the inheritance hierarchy would be uniquely identified so that they could be stored in the same database table.
To be able to manipulate a collection of Domain objects they can be represented in a collection class. While the Framework includes collection classes in the System.Collections namespace including ArrayList, SortedList, and Dictionary among others, there are advantages to creating a custom strongly-typed collection class including:
  • Strong-typing. As the name implies, a custom collection can be strongly-typed, meaning that only objects of a specific type are allowed to be added to the collection.
  • Custom Manipulation. A strongly-typed collection class also provides the opportunity to add custom behaviors to the class, for example, by providing custom sorting capabilities.
To create the strongly-typed collection you can use the power of implementation inheritance. By deriving a class from CollectionBase, ReadOnlyCollectionBase, or DictionaryBase and then overriding and shadowing various members you not only can enforce the type of objects in the collection but take advantage of functionality that Microsoft has already included in the .NET Framework. For example, Microsoft uses the CollectionBase class as the base class for over 30 of its own strongly-typed collection classes in the .NET Framework. Each of these three classes is marked as abstract (MustInherit in VB) and therefore can only be used in an inheritance relationship. 

No comments:

Post a Comment