When we speak of the Domain-Oriented Programming and Problem as you see fit, usually passes through a superficial understanding of the subject.
So I decided to write in a more practical about it and implement some solutions based on Domain-Oriented Programming.

To start, let’s define what we will have a new layer in our application. And this layer is called the Domain.
Let the diagram would look like layers of our programming.

Domain Model

UML Diagram

In the domain layer will be our problems, the idea is to transfer the problems of implementation of our business logic layer for Domains.
In this way we will reduce our codes in the business layer abruptly, in addition to achieving cleaner implementations.

How to Implement

We will not use any framework, but we will implement our domain layer to understand the code for solving the problem itself and so much better understand how this concept.
We will cover only part of the business layer, we will not see properly and database queries instead work with the calls and results imaginary. For an article thus able practical and not too big.

How to Start

To create our practice, we will create a refactoring scenario where we have a layer of logic and refactoring the application inserting the concept of orientation domains.
Our problem is a task management application, where multiple users receive and perform daily tasks. Then a report of work tasks performed by time, quantity and grade.

Today in our class would control GeneralReport a method called: loadTasksPerPeriodAmountAndNotes().

Our goal is to have tasks divided by the segments contained in the method name:


<cfscript>
    public function constructor() {
        this.instance.factory   = Application.factory;
        this.taskTopTen      	= [];
        this.taskTopFive    	= [];
        this.totalTopTen       	= 0;
        this.totalTopFive      	= 0;
    }

    public function loadTasksPerPeriodAmountAndNotes( startPeriod , endPeriod ) {
        var tasks = this.instance.factory.getService("Tasks").getFinalizedTasksPerPeriod(
            startPeriod : arguments.startPeriod , endPeriod : arguments.endPeriod
        );
        // First of all, we need to separate tasks as the amount of activities
        // is greater then 10 and tasks with avaliations notes equals 10 too
        loop query="tasks" {
            // load taskTopTen
            if( tasks.activitiesAmount > 10 ) {
                if( tasks.avaliationNotes == 10 ) {
                    this.totalTopTen++;
                    ArrayAppend( this.taskTopTen , this.instance.factory.getService("Tasks").get( tasks.id ) );
                }
            }
            // load taskTopFive
            if( tasks.activitiesAmount == 5 ) {
                if( tasks.avaliationNotes == 10 ) {
                    this.totalTopFive++;
                    ArrayAppend( this.taskTopFive , this.instance.factory.getService("Tasks").get( tasks.id ) );
                }
            }
            this.totalTopTen    = ArrayLen( this.taskTopTen );
            this.totalTopFive  	= ArrayLen( this.taskTopFive );
        }
    }
</cfscript>

Well, looking at the code above, we believe that we have within our controller logic, this logic when making refactoring should be abstracted and treated with a problem. Initially we had a simple logic, which could also be resolved in the data base, the idea is to demonstrate a basic problem for the understanding becomes clearer.

Now we understand how to solve this problem using the Domain layer.

The idea of having a layer of domains that can be a framework, is to remove such problems the code is to make it easier to understand, but we have to look for: Easy to understand, can not be simpler to implement.

A refactoring will not guarantee that it will reduce the amount of code files and implementation has, however, refactoring exists to improve the quality of its code, making it easier to read, lighter and well structured. In addition to allowing the application has a larger aperture to be serviced or even in some cases, the application becomes available to scalability.

We understand now that, which use Domain-Oriented Programming, you can end up writing more, but will ensure a greater division of responsibility and a problem domain.

To apply our refactoring, we need to create some private methods in our class and transfer some resources to the domain layer, let’s see how it works.

Domain.cfc


<cfcomponent>
    <cfscript>
        package Void function construtor() {
            this.problemSolution = "";
            this.object          = "";
        }

        package Domain function select( object , argumentParams ) {
            this.object          = arguments.object;
            this.problemSolution = [];
            if( IsArray( object ) == true ) {
                this.problemSolution = this.arrayobject( this.object , arguments.argumentParams );
            }
            if( IsQuery( object ) == true ) {
                problemSolution = this.queryobject( this.object , arguments.argumentParams );
            }
            return this;
        }

        package Domain function and( argumentParams ) {
            this.select( this.object , arguments.argumentParams );
            return this;
        }

        package String function $Eq( val ) {
            return 'EQ|$ ' & arguments.val;
        }

        package String function $Neq( val  ) {
            return 'NEQ|$ ' & arguments.val;
        }

        package String function $Lt( val ) {
            return 'LT|$ ' & arguments.val;
        }

        package String function $Lte( val ) {
            return 'LTE|$ ' & arguments.val;
        }

        package String function $Gt( val ) {
            return 'GT|$ ' & arguments.val;
        }

        package String function $Gte( val ) {
            return 'GTE|$ ' & arguments.val;
        }

        package Numeric function sumQuery( object ) {
            return object.recordCount;
        }

        package Numeric function sumArray( object ) {
            return ArrayLen( object );
        }

        /* ====== PRIVATE METHODS ====== */

        private Array function arrayobject( object , argumentParams ) {
            var this.problemSolution = [];
            this.problemSolution = this.realizeOperation( this.object , arguments.argumentParams );
            return this;
        }

        private Array function realizeOperation( object , argumentParams ) {
            var structParam = {
                   index : ListGetAt( param , 1 , "|$" ) ,
                argument : ListGetAt( param , 2 , "|$" )
            };
            switch( structParam.index ) {
                case 'EQ' :
                    return this.arrayobjectEqual( object , structParametro.argument );
                break;
                case 'NEQ' :
                    return this.arrayobjectNotEqual( object , structParametro.argument );
                break;
                case 'LT' :
                    return this.arrayobjectLessThen( object , structParametro.argument );
                break;
                case 'LTE' :
                    return this.arrayobjectLessThenOrEqual( object , structParametro.argument );
                break;
                case 'GT' :
                    return this.arrayobjectGreaterThen( object , structParametro.argument );
                break;
                case 'GTE' :
                    return this.arrayobjectGreaterThenOrEqual( object , structParametro.argument );
                break;
            }
        }

        private Array function arrayobjectEqual( object , argumentParams ) {
            var this.problemSolution = [];
            for( idx = 1; idx < = ArrayLen( this.object ); idx++ ) {
                if( this.object[idx] == arguments.argumentParams ) {
                    AppendArray( this.problemSolution , this.object[idx] );
                }
            }
            return this.problemSolution;
        }

        private Array function arrayobjectNotEqual( object , argumentParams ) {
            var this.problemSolution = [];
            for( idx = 1; idx <= ArrayLen( this.object ); idx++ ) {
                if( this.object[idx] != arguments.argumentParams ) {
                    AppendArray( this.problemSolution , this.object[idx] );
                }
            }
            return this.problemSolution;
        }

        private Array function arrayobjectLessThen( object , argumentParams ) {
            var this.problemSolution = [];
            for( idx = 1; idx <= ArrayLen( this.object ); idx++ ) {
                if( this.object[idx] < arguments.argumentParams ) {
                    AppendArray( this.problemSolution , this.object[idx] );
                }
            }
            return this.problemSolution;
        }

        private Array function arrayobjectLessThenOrEqual( object , argumentParams ) {
            var this.problemSolution = [];
            for( idx = 1; idx <= ArrayLen( this.object ); idx++ ) {
                if( this.object[idx] <= arguments.argumentParams ) {
                    AppendArray( this.problemSolution , this.object[idx] );
                }
            }
            return this.problemSolution;
        }

        private Array function arrayobjectGreaterThen( object , argumentParams ) {
            var this.problemSolution = [];
            for( idx = 1; idx <= ArrayLen( this.object ); idx++ ) {
                if( this.object[idx] > arguments.argumentParams ) {
                    AppendArray( this.problemSolution , this.object[idx] );
                }
            }
            return this.problemSolution;
        }

        private Array function arrayobjectGreaterThenOrEqual( object , argumentParams ) {
            var this.problemSolution = [];
            for( idx = 1; idx < = ArrayLen( this.object ); idx++ ) {
                if( this.object[idx] >= arguments.argumentParams ) {
                    AppendArray( this.problemSolution , this.object[idx] );
                }
            }
            return this.problemSolution;
        }

        private Array function castQueryToArray( query ) {
            var arrDataSet  = [];
			var structTemp	= {};
			var recordSet   = arguments.query;
			var fieldList 	= recordSet.columnList;
			loop query="recordSet" {
				structTemp = {};
				for( var field in fieldList ) {
					structTemp[ field ] =  recordSet[ field ];
				}
				ArrayAppend( arrDataSet , structTemp );
			}
			return arrDataSet;
        }
    </cfscript>

    <cffunction name="queryobject" access="private" returntype="query">
        <cfargument name="object"            type="query" required="true" />
        <cfargument name="argumentParams"    type="string" required="true" />
        <cfset var this.problemSolution />
        <cfquery name="problemSolution" dbtype="query">
            SELECT * FROM this.object WHERE arguments.argumentParams
        </cfquery>
        <cfreturn this.problemSolution />
    </cffunction>
</cfcomponent>

Domain made ​​our class, we refactor our business class:


<cfcomponent extends="Domain">
<cfscript>
    public function constructor() {
        super.construtor();
        this.instance.factory   = Application.factory;
        this.taskTopTen      	= [];
        this.taskTopFive    	= [];
        this.totalTopTen       	= 0;
        this.totalTopFive      	= 0;
    }

    public function loadTasksPerPeriodAmountAndNotes( startPeriod , endPeriod ) {
        var tasks = this.instance.factory.getService("Tasks").getFinalizedTasksPerPeriod(
            startPeriod : arguments.startPeriod , endPeriod : arguments.endPeriod
        );
        this.taskTopTen   = super.castQueryToArray( super.select( tasks , this.quantifyGreaterThen(10) )
			.and( this.avaliateEqualsTo(10) )
			.problemSolution );
        this.taskTopFive = super.castQueryToArray( super.select( tasks , this.quantifyEqualsTo(5) )
			.and( this.avaliateEqualsTo(10) )
			.problemSolution );
        this.totalTopTen	= super.sumArray( this.taskTopTen );
        this.totalTopFive   = super.sumArray( this.taskTopFive );
    }

    private String function quantifyGreaterThen( param ) {
        return super.$Gt( arguments.param );
    }

    private String function quantifyEqualsTo( param ) {
        return super.$Eq( arguments.param );
    }

    private String function avaliateEqualsTo( param ) {
        return super.$Eq( arguments.param );
    }
</cfscript>
</cfcomponent>

As we saw above, did the focus on problem solving Domain layer and in our public method of the business layer, we will leave only a basic implementation responsibilities to private methods and / or the domain layer.

Folks, this is the concept, is not a rule but rather how to develop the understanding that problems are solved by those who must resolve internal problems and responsibilities should be directed to private classes.

Only attend your class domains should be as generic as possible so that deal with different situations without having a domain class for each class its own.

Recalling also that the Domain-Oriented Programming, must not only be applied to the business layer … if need be the same in other sections, please use.

The implementation that we did in this article, is pretty simple, I did not do anything too complex not to get too big, so use it to study and to form a concept in their heads. In real life, this kind of definition is much more complex.

I have not tested the codes at the moment I can not test them.

by Paulo Teixeira
Original post here.

Leave a Reply