Service Area Architecture Pattern – The Basics

The Basics

This is a technical introduction of the SAAP code pattern. SAAP stands for Service Area Architecture Pattern. It was inspired from the introduction of the Area concept introduced in MVC 2.0. This programming pattern takes many of these same benefits of tight association and extends it to other parts of programming, particularly that of the services. SAAP was disclosed in our most recent patent application and is currently patent pending. Because of its simplicity and importance, we are in the process of making large portions of it available to the public. So, feel free to experiment with this pattern and build it into your applications.

SAAP is a direct substitute for SOLID code pattern. SAAP possesses many of the ideals of SOLID, but in a slightly different way. In addition to code reuse and testability, SAAP has several other goals in mind.

  • Accessible: SOLID is complex and hard to work with. Ask the top developers of any given team and they might disagree, “No, SOLID is easy. You just need to do it right.” Ask the average developer, you may get a different answer. Try to hire a qualified SOLID developer, and your options diminish. I discussed at length with many skilled top developers, problems connected to SOLID and TDD. This is not a problem unrecognized in the industry. There is also a strong group thought, lead by people who want to showcase their skills to say: “You’re just doing it wrong.” In my view, the goal shouldn’t be just to do it right; it should be to make doing right easier. This is an advantage SAAP has over SOLID.
  • Traceable: Dependency injection frameworks are commonly used in applications that follow SOLID. It is critical component in making SOLID testable. However a major drawback is that it greatly limits codes traceability. These frameworks come in many flavors with no real standard. There is always a degree of complexity involved and add an element of a learning curve. The SAAP context effectively removes the need for dependency injection frameworks. The Context object type is easy to use. It maintains strong traceability. It is easy to identify when and where the behavior was assigned or modified.
  • Readable: An individual can comprehend the scope of a given code task in far less effort. Better communication means that a information can be conveyed at a much lower price. The service logic is easier to follow, defects are more identifiable, new team member can be on boarded in less time. Properly associating the various resources for a given task (or service) is key. Often times SOLID applications organize types by their general functional purpose, rather than the task they are solving: So, Models are by Models; Services are by Services, Interfaces by interfaces, and so on… SAAP favors organizing associating types by task opposed to by functional purpose. The idea is to effectively make a service into its own sub-program. All of the specific components of a service reside in close proximity. This is accomplished with:
    • Nested, static and partial classes are extensively used to structure, organize as well as verifying proper behavior and relationships between the various classes.
    • Logically participating code in nested files, while following a simple naming convention (which will be discussed in subsequent blog postings).

    A programmer can spend more time focusing on the problem at hand and less on programming logistic. This holistic approach provides a simplified way of depicting a logical flow of the application. For that matter, it is easier to identify dead code and removed. Less code means less noise.

  • Writeable: Writeable is synonymous with economical. How much effort does it take explain your ideas to the computer, or for those who have to maintain the code after you? Positioning code for refactoring is a good thing. Improving writability means reducing the cost of development and repaying technical debt. It can be implemented incrementally. There does not need for a big investment. The names are regular, predictable and logical. Most importantly, it is intuitive. It is the way the problem wants to be solved.

SAAP does things a little differently from SOLID and TDD. Here are some concreate instances:

  • First, the need for injection frameworks has been removed. This is in large part to a re-interpretation or ignoring the SOLID – Open Close Principle. Also, Interfaces are not required in the same way to provide a means of setting up code contracts.

    • Instead, SAAP uses a Model for methods called a Context which can be easily reconfigured for any given test.
  • Second, it is recommended in TTD methods are not static.

    • In SAAP, virtually all methods are static and resolved through a Context object.

Scaffolding

Let’s look at an example of the Scaffolding. Below is a simple example of common components.

namespace MyApplication
{
    public static partial class MyService
    {
        public delegate Response MakeRequest(Context context, Request request);
 
        public class Context
        {
            public virtual MakeRequest MakeRequest { get; set; } = Definition.MakeRequest;
        }

        public static class Definition
        {
            public static Response MakeRequest(Context context, Request request)
            {
                var output = new Response();
                //Code logic goes here.
                return output;
            }
        }

        public class Request { /* Request properties go here. */ }
        public class Response { /* Code variables go here. */ }

        public class Model { /* Code variables go here. */ }
    }
}

The structure is simple because SAAP is simple.

MyApplication: Here is an example SAAP Service configuration for the MyApplication namespace. The namespace is obviously arbitrary. What is important to note here the generally the service container types are connected to a raw namespace. Generally, model and behavioral components are defined in nested classes, and accessible with the service name as part of the namespace. For instance, The Context in MyService can be fully qualified as: MyApplication.MyService.Context.

MyService: MyService is the service container type. It provides a placeholder for the associated nested types. It provides the confines of an application sub-program. It can still refer to outside contexts and models. Referring to them is just as easy as using any other OO pattern. Again, a key benefit is that data and behavior is virtually always loosely coupled, but tightly always tightly associated. Accounting models and Context will not be confused with sales models and method; however, Accounting is still free to use Sales models and Context with a clear separation of responsibility.

Delegates: Immediately inside of the service container type is where the delegates should be stored. The delegates provide a simple way to assign a method to a Definition to a corresponding property of a Context. Here you will note that the first parameter is of type Context. This is by design. It is not required. There is a tremendous payback when it is appropriate, however. Adding this context parameter allows the method to be modified for testing, and virtually eliminates the need for inversion of control. There are other benefits to the SAAP framework. However, having the ability to dump Dependency Injection frameworks is perhaps at the top of the list for me. We will see how this works when we go over testing.

Context: Below the Delegates is the Context class. The Context class can be simply defined as a Model for its actions. Just as Data Models contain primitives and references to other Data Models, a Context will hold references to methods and other Contexts. The Context properties declared virtual so they can be redefined in an abbreviated initialization. What is also obvious here is the corresponding method Delegate and Definition. Now you might be wonder; can I reuse a delegate? The answer is: of course; provided that the signatures are compatible. There are shortcuts and variations that can be used. This exoskeleton is meant to provide a very basic example of how the pattern might work.

Definition: Below the Context is the Definition. A Definition is itself a kind of a static model. It can hold methods and nested static classes (which would be used to relate methods of a common purpose). This will be demonstrated in a follow-up blog posting.

Request & Response: Below the Definition is the Request type. A Request type is usually coupled with a complementary Response type, which is describe below the Request. A Request is a package of data (a model) which will have a differing output. A request may contain a number of optional properties, which would reduce the need overloading methods. Overloading methods increases the number of methods required. (Additionally the Context type does not support overloading methods, that would require supporting multiple properties of the same name but different type.)

Model: Below the Request and Response is the Model the model is primary data type for a given service.

It should be noted while name reuse is encouraged, adding additional model types works perfectly well with this model.

In SOLID TDD, there is a concept of: Services, Resources, Interfaces, Models, View Models. In SAAP, a resource is depicted as in reality being a Service; View Model as a Model (of a given service) and an Interface is no longer required for injection.

An example of a resource is the machine clock. The clock represents a potential problem for testability. In a scheduling application, the current time may affect the behavior of the application. Simply put, the only way to provide automated unit testing is to be override the current time.

A clock can be set using, a similar form as the example above. Notice the primary difference is the signature of the Now method. There is no Context parameter which means that the method cannot be extended to call other Context methods.

using System;

namespace MyApplication
{
    public class Clock
    {
        public delegate DateTime Now();

        public class Context
        {
            public virtual Now Now { get; set; } = Definition.Now;
        }

        public static class Definition
        {
            public static DateTime Now() { return DateTime.Now; }
        }
    }
}

The Clock method can be abbreviated to be more informal:

using System;
namespace MyApplication
{
    public class Clock
    {
        public class Context
        {
            public virtual Func<DateTime> Now { get; set; } = () => DateTime.Now;
        }
    }
}

Sometimes it may be preferable to define a Context method property using a Func or Action. A Context method property can also be assign using a lambda or anonymous Function (as long as it matches the required signature). This is up to the discretion of the developer; however, the bigger the Service Area, generally the more formally it should be implemented (using delegates). This is important. No matter which level of formality a service is constructed with, the use of inline functions remains an economical and traceable way of simulating behavior for automated testing.

This brings us to a major difference between SOLID and SAAP. The SOLID Open Close Principle (OCP) says a class should be open for Extension, Closed for modification. SAAP, in a very real sense, ignores this practice, but effectively has the same result. SAAP allows for the behavior to be modified through the assignment of delegate properties during or after initialization. SOLID generally requires a constructor to provide a series of interfaces to modify the internal behavior of a given service. Two reasons for this are:

  • First, OCP is meant to provide a reliable boilerplate that can be reused from application to application.
  • Second, Event Driven development means makes following the flow of an application exceedingly difficult.

In designing SAAP, both of the concerns are addressed.

Outside of a utility framework, there are only a select few methods that are likely to be reused from application to application. In a business application, very little is reused outside of the application. In the case of the Clock example, the Clock Service could easily be included in a utility library (or in a simple copy and paste). Other resource oriented operations, such as IO, can be quickly and easily adapted to use the SAAP pattern while remaining portable for use in other applications.

A Functional Example

Now let’s review a more functional example:

namespace MyApplication
{
    public static class WorkShift
    {
        public delegate Response Function(Context context, Request request);

        public class Context
        {
            public Clock.Context ClockContext { get; set; } = new Clock.Context();
            public Function Run { get; set; } = Definition.Run;
            public Function First { get; set; } = Definition.First;
            public Function Second { get; set; } = Definition.Second;
            public Function Third { get; set; } = Definition.Third;
        }

        public static class Definition
        {
            public static Response Run(Context context, Request request)
            {
                var now = context.ClockContext.Now();
                Response output;
                if (8 < now.Hour)
                    output = context.First(context, request);
                else if (now.Hour < 16)
                    output = context.Second(context, request);
                else
                    output = context.Third(context, request);
                return output;
            }

            public static Response First(Context context, Request request)
            {    
                var message = $"{request.EmployeeName} ({request.EmployeeId}) is working first shift.";
                var output = new Response { Message = message };
                return output;
            }
            public static Response Second(Context context, Request request)
            {    
                var message = $"{request.EmployeeName} ({request.EmployeeId}) is working second shift.";
                var output = new Response { Message = message };
                return output;
            }

            public static Response Third(Context context, Request request)
            {
                var message = $"{request.EmployeeName} ({request.EmployeeId}) is working third shift.";
                var output = new Response { Message = message };
                return output;
            }
        }
        public class Request
        {
            public int EmployeeId { get; set;  }
            public string EmployeeName { get; set; }
        }

        public class Response
        {
            public string Message { get; set; }
        }
    }
}

Here we see an actual example. This is a WorkShift simulator. The main method is Run. The first shift ends at 8:00 am, the second at 4:00 pm, and the third at midnight. As explained before, the behavior is dependent on the time of day. In this case, functional testing is impractical.

There are a couple of other things that are noteworthy:

  • First, there is only one delegate type of Function. There would be little consequence to having redundant delegates. In this case, it is more concise to stay with the one.
  • Second, the Context parameter could be removed from the First, Second, and Third methods. This would change the signature and require a second delegate. Additionally, removing the Context parameter would make limit that methods ability to call other methods (while remaining testable).

Running the application at each timeframe would be expensive. Automation is preferable. Please note the following code:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace MyApplication
{
    [TestClass]
    public class TestWorkShift
    {
        private WorkShift.Context _context = new WorkShift.Context();
        private WorkShift.Request _request 
                = new WorkShift.Request { EmployeeId = 123456, EmployeeName = "Max Powers" };

        /// <summary>
        /// Demonstrates simple run, and substitution of resource method.
        /// </summary>
        [TestMethod]
        public void TestRun()
        {
            var expected = "Max Powers (123456) is working first shift."; 
            // Here is a simple example of modifying resource logic.
            _context.ClockContext.Now = () => DateTime.Parse("03/12/2016 7:00 am");
            var response = _context.Run(_context, _request);
            Assert.AreEqual(expected, response.Message);
        }
    }
}

This simple test illustrates how SAAP injection works. It is easy to override the Now function with a simple lambda expression. Since the _context object is passed as a parameter, and the Now method is associated with the child of the _context object, the reassigned now method will be accessible to the called method internally. Alternatively, the Clock.Context could be overridden and reassigned to the ClockContext object. However, in this specific case it is probably preferable to stay with inline function. The other methods (First, Second, Third, or Run) can also be by: simple property assignment, assigning some or all in the initialization, as well as overriding the class.

Conclusion

SAAP, as you can, see is pretty easy to use. There is more to this coding pattern, designed to solve other problems. In upcoming vides and blog postings we will discuss: file code segmentation, natural parameterization, and generics. Stay tuned …