The Developer Day | Staying Curious

TAG | transaction-script

Feb/10

18

Domain Model Logic Patterns

A great thing about patterns is that it provides a vocabulary. It allows developers to communicate efficiently. Complex ideas can be described at a high technical level without going into small details. Patterns are like butterflies and come in many colors, sizes and shapes and there’s.. lots of them. As butterflies patterns are grouped into families. One of such families is Domain Logic Patterns described in Martin Fowler’s book Patterns of Enterprise Application Architecture.

It’s one of the fundamental families because virtually every application written is described by any of the three patterns that belong to it. To say it simply there are three major ways how an application can be designed. What is more interesting that each of those patterns can be sorted by the amount of effort required to change the application depending on it’s complexity. Deciding which pattern to choose can be tricky because these patterns depend on various characteristics of the project being developed. The size, the complexity, the lifetime of a project and most importantly the skills of the developers.

Transaction Script

Transaction script organizes business logic by routines where each routine handles a single request from the presentation layer. It is the most common way how software beginners write applications. Required steps to perform a task are identified and expressed using language semantics. Common routines can be broken into subroutines. Transaction Scripts can be organised as classes or as global functions. A single file can contain many Transaction Scripts at once. A lot of developers may think they are proficient OOP users while in reality they write data centric Transactions Scripts wrapping them into classes which could be just as easily replaced with simple procedures.

However Transaction scripts have benefits. It’s a simple procedural model so most developers can understand it, it works well with a simple data access layer using Row Data Gateway or Table Data Gateway.

An example transaction script may be booking a hotel room where a single procedure is responsible for checking the availability of the room, calculating the price and updating the database.

class Hotel
{  
	public function __construct(Data_Access_Gateway $gateway)  
	{  
		$this->_gateway = $gateway;  
	}  
	public function bookRoom($userId, $fromDate, $toDate)  
	{  
		$roomId = $this->_gateway->_getRoomIdBetweenDates($dateFrom, $dateTo);  
		if (!$roomId) {  
			return false;  
		}  
		$days = $this->_getAmountOfDays($fromDate, $toDate);  
		if ($days < = 7) {
			$price = $days * 100;
		} else { 
			$price = $days * 80;
		}
		$data = array(
			'userId' => $userId,  
			'roomId' => $roomId,
			'fromDate' => $fromDate,  
			'toDate' => $toDate,  
			'price' => $price,  
		);  
		$bookingId = $this->_gateway->insert('bookings', $data);  
		return $bookingId;  
	}  
}

Table Module

A Table Module is many ways the middle man between a Transaction Script and a Domain Model. A Table Module tends to organize domain logic with one class per table in the database. While a Transaction Script will have one class Hotel to do reservations a Table Module may have more fine grained classes like Hotel, Booking, Room. Even though these classes may be singular they do not actually represent individual entities and manages whole collections of entities. For example a Booking class would be responsible for mosts actions performed on the bookings database table. Like a Transaction Script a Table Module is a very data centric model approach and usually has a strong connection with the data access layer.

Table Module has a few benefits over a Transaction Script. There’s less duplication, domain logic is more organized and structured around domain entities. It works well with simple data access layers. However, Table Module doesn’t give you full power of objects in organizing complex domain logic. It lacks instance to instance relationships, has weak support for polymorphism.

To show an example of Table Module let’s re-factor the Transaction Script:

class Hotel
{  
	public function __construct(Data_Access_Gateway $gateway, Booking $booking)  
	{  
		$this->_gateway = $gateway;
		$this->_booking = $booking;
	}  
	public function bookRoom($userId, $fromDate, $toDate)  
	{  
		$roomId = $this->_booking->getRoomBetweenDates($fromDate, $toDate);
		if (!$roomId) {  
			return false;  
		}  
		$days = $this->_getAmountOfDays($fromDate, $toDate);  
		if ($days < = 7) {
			$price = $days * 100;
		} else { 
			$price = $days * 80;
		}
		$bookingId = $this->_booking->addBooking($userId, $roomId, $fromDate, $toDate, $price);  
		return $bookingId;  
	}  
}
class Booking
{  
	public function __construct(Data_Access_Gateway $gateway)  
	{  
		$this->_gateway = $gateway;  
	}
	public function getRoomBetweenDates($dateFrom, $dateTo)
	{
		return $this->_gateway->getRoomBetweenDates($dateFrom, $dateTo);
	}
	public function addBooking($userId, $roomId, $fromDate, $toDate, $price)  
	{  
		$data = array(
			'userId' => $userId,  
			'roomId' => $roomId,
			'fromDate' => $fromDate,  
			'toDate' => $toDate,  
			'price' => $price,  
		);  
		$bookingId = $this->_gateway->insert('bookings', $data);  
		return $bookingId;  
	}  
}

Domain Model

Domain Model organizes domain logic into well defined domain entities that contain both behavior and data. Usually each entity class represents a single entity in the data layer. Domain Model creates a hierarchy of inter connected objects that interact with each other by triggering behaviors on each other. Domain Model shines over Transaction Script and Table Module when it comes to handling domain complexity. It enables the full power of object oriented programming and all available design patterns. Even though Domain Model may sound like a silver bullet to all software development problems it is not. Learning to design applications using Domain Model requires a “paradigm shift” to start looking for entities and behaviors instead of looking for routines. The main downside of Domain Model is the way it works with relational databases. To compare I would like to quote Martin Fowler:

In many ways Domain Model treats the relational database like a crazy aunt who’s shut up in an attic and whom nobody wants to talk about.

Domain Model tries to be data layer agnostic. This is what object relational mappers also known as ORM’s as Hibernate try to solve by hiding the data access layer away.

To show an awkwardly simple example of Domain Model let’s re-factor the Table Module approach. To keep the example simple I’ll ignore the data access layer.

class Hotel
{  
	protected $_hotelId;
	protected $_rooms;
	public function bookRoom(User $user, $fromDate, $toDate)  
	{  
		$room = $this->_getRoomBetweenDates($fromDate, $toDate);
		if (is_null($room)) {  
			return false;  
		}  
		$booking = $room->bookRoom(User $user, $fromDate, $toDate);
		return $booking;  
	}
}
class Room
{
	protected $_roomId;
	protected $_bookings = array();
	public function bookRoom(User $user, $fromDate, $toDate)
	{
		$days = $this->_getAmountOfDays($fromDate, $toDate);
		if ($days < = 7) {
			$booking = new Booking($user, new ShortBookingStrategy($user, $days));
		} else { 
			$booking = new Booking($user, new NormalBookingStrategy($user, $days));
		}
		return $booking;
	}
}
class NormalBookingPriceStrategy extends BookingPriceStrategy
{
	public function getPrice()
	{
		$price = $this->_days * 80;
		if ($this->_user->isLoyal()) {
			$price = $price / 2;
		}
		return $price;
	}
}
class ShortBookingPriceStrategy extends BookingPriceStrategy
{  
	public function getPrice()
	{
		return $this->_days * 100;
	}
}

As you can see there is an instant explosion of classes and the data access layer is out of the picture. Responsibility to efficiently load and keep track of loaded entities is left to object mappers like Hibernate.

The End

Everyone of these domain model patterns have their benefits and downsides. It takes experience to be able to decide which approach to take. For those who are new to Domain Model it is advised to have an experienced team member who is able to guide you through it. While Transaction Script or Table Module can seem like a good solution at some point it may not be as effective later on. It is important to recognize when a different domain model approach might work better. It’s been a long ride for me. Through the years I’ve learned a few things about designing software but I still don’t feel very comfortable writing about it. I would love to hear feedback from you how this article and these examples could be improved to enable better understanding between the different modeling approaches.

, , , Hide

Find it!

Theme Design by devolux.org