the action_executor pattern

12
5/13/12 The Action/Executor Pattern 1/12 msdn.microsoft.com/en-us/library/cc984279.aspx The Action/Executor Pattern 0 out of 2 rated this helpful - Rate this topic Driss Zouak Solution Manager/Architect, Avanade September 2008 Summary: The Action/Executor pattern identifies a strategy for mapping use cases to code, allowing better visibility and agility. Also, it addresses the issues of contaminating entities and skipping over proper use of transactions. (12 printed pages) Contents Overview Introduction Dependency Inversion Pattern and References Service Locator Pattern Dealing with CRUD Dealing with Retrieval Regarding TransactionScope Conclusion Overview Have you ever been handed an application where you looked at the design and implementation, and felt that you were seeing plenty of square pegs smashed into round holes or just straight into the boards? Or have you ever had that “this just feels wrong” feeling when you found yourself adding methods to entities that did not really have a lot to do with the entities themselves, but there was no other reasonable place to put them? Have you ever skipped using transactions properly, because it just was not really evident where they were supposed to go? The frustration often comes from having a design model that is incomplete, without knowing it. The standard model for separating presentation, business, and data layers only divides responsibilities one way, and very coarsely—leaving patterns of implementation to bring about these frustrations. The pattern that is presented in this article takes a step into the business and data layers, and further subdivides them—addressing these common issues, and describing where complementing technologies and patterns fit in. It is called the Action/Executor pattern. Too often, the business layer ends up contaminated with business logic strewn everywhere; the relationship between the use cases and implemented logic lost; entities with methods responsible for persisting themselves, but without context; and some “I didn’t have anywhere better to put it, but I must be able to call this stored procedure”–type methods. The data layer often ends up being a pass-through to the stored-procedure set of classes. In these situations, the business layer has become more of a non-UI layer and a not-that-much- data layer, or a whatever-worked-at-the-time layer. The Action/Executor pattern divides up the responsibilities for the business layer, which consists of concepts (entities) and atomic or composite actions that are directly related to use cases (actions), and it splits the data layer into mapping entities to their persistence store (persisters) and the action complement and transaction owner (executors). It is the Executor

Upload: manitcor

Post on 27-Oct-2014

137 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

1/12msdn.microsoft.com/en-us/library/cc984279.aspx

The Action/Executor Pattern

0 out of 2 rated this helpful - Rate this topic

Driss ZouakSolution Manager/Architect, Avanade

September 2008

Summary: The Action/Executor pattern identifies a strategy for mapping use cases to code,allowing better visibility and agility. Also, it addresses the issues of contaminating entitiesand skipping over proper use of transactions. (12 printed pages)

Contents

OverviewIntroductionDependency Inversion Pattern and ReferencesService Locator PatternDealing with CRUDDealing with RetrievalRegarding TransactionScopeConclusion

Overview

Have you ever been handed an application where you looked at the design andimplementation, and felt that you were seeing plenty of square pegs smashed into roundholes or just straight into the boards? Or have you ever had that “this just feels wrong”feeling when you found yourself adding methods to entities that did not really have a lot todo with the entities themselves, but there was no other reasonable place to put them? Haveyou ever skipped using transactions properly, because it just was not really evident wherethey were supposed to go?

The frustration often comes from having a design model that is incomplete, without knowingit. The standard model for separating presentation, business, and data layers only dividesresponsibilities one way, and very coarsely—leaving patterns of implementation to bringabout these frustrations.

The pattern that is presented in this article takes a step into the business and data layers,and further subdivides them—addressing these common issues, and describing wherecomplementing technologies and patterns fit in. It is called the Action/Executor pattern.

Too often, the business layer ends up contaminated with business logic strewn everywhere;the relationship between the use cases and implemented logic lost; entities with methodsresponsible for persisting themselves, but without context; and some “I didn’t haveanywhere better to put it, but I must be able to call this stored procedure”–type methods.The data layer often ends up being a pass-through to the stored-procedure set of classes. Inthese situations, the business layer has become more of a non-UI layer and a not-that-much-data layer, or a whatever-worked-at-the-time layer.

The Action/Executor pattern divides up the responsibilities for the business layer, whichconsists of concepts (entities) and atomic or composite actions that are directly related to usecases (actions), and it splits the data layer into mapping entities to their persistence store(persisters) and the action complement and transaction owner (executors). It is the Executor

Page 2: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

2/12msdn.microsoft.com/en-us/library/cc984279.aspx

concept—explained further in the article—that makes the difference in this approach.

Figure 1 is a high-level diagram that shows the responsibilities and their layers:

Figure 1. High-level diagram showing concepts and data, steps and logic

NOTE Usually, it takes two—sometimes three—meetings with development teams, before itclicks what it is about this approach that is just different enough that it becomes a “holy cow,how did we not happen upon this before?”

Introduction

As one might expect, most reasonably well-made applications have the standard layeredstructure that Figure 2 shows:

Figure 2. Standard layer structure of applications

Each layer does what you might expect:

· The interface layer is responsible for managing how the application is going tointeract with the outside world—be it through Web pages, a smart client, Web services, oranother messaging approach.

· The business layer starts out with entities representing a mix of real data entitiesand conceptual entities.

Page 3: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

3/12msdn.microsoft.com/en-us/library/cc984279.aspx

· The data layer ends up being basically a mapper of the entities to the database. Thearrows in Figure 2 show the dependency between the layers.

It would seem that all is good, because everything is separated. But is it, really? The entitiesend up with methods such as Delete, Select, or Get, and either a Save or an Insert andUpdate to start with. Already, you see the storage mechanism showing its particularities inthe entities, because the parameters of these methods almost always are identical to thestored procedures at the end of the line. Then, the entities start having methods that do notmake any sense, but which are seen as a way around creating other entities that have asingle purpose; and they usually need their own CRUD, but in a way that is related to theentity onto which they have been grafted, and so other methods or optional parameters arecreated. Then, the business logic starts to get blended into different versions of the Savemethod or different cases (ifs) of it, and you find yourself with business rules spreadrandomly throughout your code—with no clear way for the developer who comes after you toknow how it all ties back to the original use cases, and thus how to map changes to the rightplace in the code. Over a couple of months or years, this very often leads to instant legacycode (“I don't know if that code really does anything anymore, but leave it, just in case”).

Let’s look at an example. Say that you have an invoice that you want to submit; so, the Webpage creates an invoice object, populates it with your new info, and calls Invoice.Save().Yay! We’re done. Ah, not quite. The requirements just got updated; it seems that now wealso have to create a dummy customer, if this is the first invoice for a customer, but we donot have the customer info yet. Okay, modify the Save method to have a switch statement.Another change; now, if the credit limit is over a certain amount and we're doing an invoicesubmission, instead of posting an invoice, then... Oh, another new requirement; now, wehave another situation in which we must add a Purchase Order (PO), but no dummy customeris allowed into the mix. When it saves, we must save also the invoice and update thecustomer (in some cases, depending on the circumstances)... Now, we have Save1, Save2,Save3 (effectively), and a number of other clumsy methods from the point of view of “clearand easily mapped to the requirements.”

By the way, where's the database transaction in all of this? A significant number ofapplications that are written today do not really use one, because it is not clear where it goes,or it is clumsy and complex, and in large part because of the constraints of the model that isused. Invariably, it gets declared by the entity in these cases, which starts it all off, andeverything is contained within their method; and, when you step back, it just does not makesense. Whether or not this is done semi-abstractly by telling your autogenerated data-accessobject to start a transaction, it still means that your business layer fully understands thatyour data layer is talking to a database.

The result is polluted entities, at the very least. It's not uncommon to see a couple ofdifferent types of Save methods, and then to hear how, when the project was transitioned tosomeone else to support, things went from bad to worse, because new entities and methodswere wired to the “wrong” versions of Save and other methods—some of which performedcascading saves when not expected or created dummy values where not expected (or otherrandom sets of changes) or had pieces that were outside of the transaction, and so on. Thereason is simple: It is that the entity that represents a concept was given business-activityresponsibilities, and that is not its intent—which is why things start to break down from thebeginning. Sometimes, senior developers refer to this as the uncomfortable feeling that theyhave, because they know that it should not be this way, but it is; and they accept it, thoughthey hope to find a better way one day.

People who have had sufficient pain with this approach usually turn to some form of thefollowing, as Figure 3 shows:

Page 4: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

4/12msdn.microsoft.com/en-us/library/cc984279.aspx

Figure 3. Modified layer structure

Now, the business logic is grouped into classes that are distinct from the entities and use theentities to get a job done; but, other than that, their purpose is not clear. This is better,because now we can have a set of process classes that help us avoid the issue of having aSave1, Save2, and so on. It also helps us address the other classical situation of havingbusiness activities to perform that don't really map simply to a specific entity—such as, say,determining credit-worthiness.

The challenge here becomes two key things, the first of which is transactional responsibility.Who is responsible for creating the transaction that ensures that all of the steps that must betaken to complete the process are taken; and can we do it without revealing (in the businesslayer) that we are dealing with a database to allow us later flexibility for Web services orother approaches? The usual (75+ percent) answer is the process class (leaving asideTransactionScope until later), which means that the business layer knows some concretestuff about the data layer because it ends up creating a database transaction. Twenty-fourpercent of the time, it is something in the entities, because the entities still have CRUDmethods on them. One percent is left to the truly creative approaches.

This brings us to the second challenge. The problem with entities having CRUD methods andprocess classes is that the entity methods can be used outside of the confines of the processclasses—potentially resulting in the storage of invalid results. Is it valid to save an invoiceoutside of the SubmitInvoice process? Maybe; but then, how can you validate when? Thepremise is: Do not make something available to be misused and misunderstood, because, ifyou do, it will (at some point).

Another of the problems with this approach is: What defines a process? The idea is notanchored to anything, in most cases, and usually ends up being any method that cannot beattached clearly to an entity or is complex enough to involve a number of entities relativelyequally. Yet another issue is: Where do you put those pesky little extras, such as callingspecial stored procedures that are not related to a particular entity, but are necessary as partof the “process.” They are part of the data-level activities, such as recalculating credit scoresor archiving a previous “set of data”; they must be included as part of the transaction.

This is where the Action/Executor pattern starts to provide immediate value—by breaking upthe business layer and the data layer into four areas, as Figure 4 shows:

Page 5: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

5/12msdn.microsoft.com/en-us/library/cc984279.aspx

Figure 4. Modified layer structure, showing broken-up business and data layers

There are two key changes here from the previous model. The first seems syntactic, goingfrom process to actions, but there is some method to the madness. The idea is that an actionis a logical atomic block of work, as defined in a use case; thus, a use case has one or moreactions that map to it. Actions use entities to accomplish tasks. Therefore, all of the logicwithin an action is business logic for both validation and execution of that action. An actionhas only two methods—validation and execution (surprised?)—and all entities that arerequired are provided to the constructor, so that changes "don't occur" (this is an in-principalpoint, not a technically accurate one) or are retrieved by the action itself.

The action’s responsibility in terms of validation is to ensure that everything that has beenprovided meets the conditions to allow the action to be executed. If validation fails, usuallytwo pieces of information are returned: an indicator of success or failure (usually a Boolean)and a user-readable error string (as an out string parameter). The objective is to provide the“use-case–oriented reasons for failure that can be presented back to the user” and not an“Error 1571 - Null Reference Exception” type of error. Validation is also where complicatedsecurity rules can be checked to determine whether or not this user, at this time, is allowedto do what is being requested.

The action’s Execute method is responsible for preparing everything to happen, theninvoking the atomic permanence step (that is, calling the executor), and then doing whateverelse must be done before control is returned. Preparing can consist of looking up otherinformation, creating or calculating whatever is needed, creating audit messages that will bepersisted along with the other data updates, and so on. When everything is ready, it calls theexecutor.

The executor, which is named after the executor of a last will and testament, knowsconcretely what type of transaction is needed—be it a database transaction, Web servicetransaction, and so on—because it is a member of the data layer. It then orchestrates theproper usage of the persisters—weaving the transaction through them, including writingwhatever audit messages (I'm big on audit messages)—and lastly is able to run whateveradditional stored procedures or other activities must be performed as part of the permanencestep and, logically speaking, for completing that atomic business action. That ability to havea nice, clean place for those “extras”—for that “recalculating credit score stored procedure”that I mentioned earlier—is key, because it avoids contaminating so many different objects.(Note that later there is a brief section that talks about TransactionScope.)

A persister is responsible for a single entity, mapping it to the appropriate persistence layer—

Page 6: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

6/12msdn.microsoft.com/en-us/library/cc984279.aspx

be that to a single table or multiple tables on multiple databases, or calling Web services orwhatever else. A persister’s job is to understand how to map the entity to that table, andnothing else, without any cascading.

As a side note, consider not having Insert and Update methods; but, instead, push this allthe way into the database by having only a Save method and a Save stored procedure.Inside the stored procedure, always pass in the unique ID and check to see if the ID that isprovided is equal to a not set value for the particular type of data entity (for example, use –1); if that is the case, insert otherwise update. Return the ID value, and set it to the IDproperty in your code, always. If it is an update, the ID will be unchanged; if it is an insert, itwill now be set properly. This helps keep code completely ignorant of potentially complexperformance-optimized schemas.

An advantage already from the perspective of design and communication is that the designmodel now can be illustrated as Figure 5 shows:

Figure 5. Diagram of design model, showing responsibilities and their layers

This means that it is easier to map between requirements and implementation, and easier totrack as well as discuss among developers, project managers, and analysts what logic iswhere and why.

From the perspective of a standard layer stack, it means the following, as Figure 6 shows:

Figure 6. Modified diagram of standard layer structure

Page 7: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

7/12msdn.microsoft.com/en-us/library/cc984279.aspx

Instead of the layers being just horizontal, there is now also conceptually a vertical layering.Actions and executors are centered on the idea of executing a set of steps first and foremost,whereas the entities and persisters are centered on data. With this in mind, let us look at howthe elements of the different layers use each other, as the Dependency Inversion pattern nowsteps in.

Dependency Inversion Pattern and References

The Dependency Inversion pattern is the idea of reversing a dependency, which concretelyhere is the idea that the business entities do not call the persisters, but instead the persisterswill take the entities as parameters for their methods. This allows the data layer to be theonly area in which there is any knowledge whatsoever of how things are stored—which reallyis the point of the data layer, is it not?

Figure 7. Layer structure, showing reversed relationships

NOTE There are a couple of key questions that always come up; they are addressed soon(wait, it’s coming).

So, Figure 7 shows that the entities never talk to the persisters; instead, the relationship isthe other way around. The interface layer only manipulates entities and actions to accomplishwhat it needs.

At this point, the approach can be used within a single DLL in Microsoft Visual Studio 2005(or later) by using solution folders to separate the various elements, if so desired. If you keepyourself honest, there is no issue; and, for really small projects, it is well worth it for you todo this.

For more sizable solutions, the chain of references resembles Figure 8:

References

Page 8: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

8/12msdn.microsoft.com/en-us/library/cc984279.aspx

Figure 8. Chain of references for more sizable solutions

The persisters need a reference to the entities as do the executors. At first pass, you mightask why, only then to realize that the actions have provided the entities to the executor forthe action to be finalized, and the executor then needs to hand them over to the persisters.

The question that of course arises is: How do you take the next step and introduce theflexibility of having either various or replaceable (flexible) data layers working with the samebusiness layer and above—allowing for a database to be used today, but a Web service to beused tomorrow or, more importantly, to allow for a mock data layer? Enter the Service Locatorpattern.

Service Locator Pattern

There are a lot of patterns that have bad names; Dependency Injection is one (it really couldbe called “Passing Objects into Constructors! Now with Polymorphism!”), and Service Locatoris another (it could be called “VB6-Like Late Binding” or “COM-Like Interfaces Are Back!” or,even better, “Lazy Class Loading with Flair!”).

The idea behind Service Locator is that your classes reference either a base class type or aninterface, and you pass a singleton—usually called the Service Locator—the type that you arelooking for, and you ask it to give you the real type that you are supposed to use. Thesingleton looks up—either in a configuration file or in a database, usually—what the type thatyou provided maps to, which is a DLL name and a specific class name. The singleton thenusually instantiates this “real class” for you and hands it back to the caller. The caller isabstracted from the real type, and the classes that are used this way are dynamicallyconfigurable (XML file, database table, and so on), instead of requiring recompilation tochange. This is great for being able to use mock persisters and executors, or for changingfrom one type of database to another or to Web services.

Figure 9 shows the relationship to the entities and so forth:

Page 9: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

9/12msdn.microsoft.com/en-us/library/cc984279.aspx

Figure 9. Layer structure, showing relationships using Service Locator pattern

Now, the actions are “bound” to the IExecutor or IExecutors, depending on whether youdecide to use either a super class pattern for all of your executors or multiple distinctexecutors. Given that usually there is only one method needed per action class, eithergrouping into executors or using a super class should fit the bill. A benefit of this strategy isthat you can provide a proxy executor (if needed) at any time by configuration with theService Locator pattern, which allows you to push the real transactional work elsewherewithout the action ever knowing. Actions could also be looked up dynamically by usinginterfaces for your actions and calling the Service Locator to "get them." This is particularlyhelpful when you have evolving tax rules or similar such "volatile" types.

NOTE The new Microsoft Enterprise Library 4.0, which is for Microsoft .NET Framework 3.5only, has a Unity Application Block, which is a Service Locator that has dependency injectionand inversion.

The library references now have become a bit more complex, as Figure 10 shows:

Page 10: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

10/12msdn.microsoft.com/en-us/library/cc984279.aspx

Figure 10. Modified library references

Dealing with CRUD

Creating, retrieving, updating, and deleting—CRUD. We have to deal with it; and, in the nextsection, we will address the retrieval part. It is recommended not to have actions named afterwhat is essentially database activity. You do not CreateInvoice, in the minds of mostbusiness people; you GenerateAnInvoice. You do not UpdateAnInvoice, unless you areModifyingExistingInvoice; otherwise, you are SubmittingInvoice orResubmittingInvoice. You do not DeleteAnInvoice; you either VoidAnInvoice or creditagainst it (or some other activity).

The intent with the action naming is to give it the business-related names that you likelyhave in your use-case or requirements document, and forcing this almost always results inthe development team thinking about these objects differently. Usually, a day or two afterthis discussion, a question arises like, “About SubmittingInvoice... Would it make sense forus to check X before sending it to the executor? It’s part of our business process that’s donemanually, but we were kind of ignoring it from a technical perspective, although I see whereit could go now.”

Some developers immediately ask, in the hopes of avoiding adoption of the model, “Aren’t wejust going to be creating a lot of unnecessary code to deal with what essentially will beunnecessary classes to manage trivial activity?”—to which the answer to the real question is:“Use code snippets and generics to speed things up.”

Dealing with Retrieval

Now, does it make sense to have an action for every single piece of data retrieval, everysingle drop-down list and everything? No, of course not. The idea is not to have a hammerand hit everything, yelling, “NAIL!”

There are circumstances in which having an action for data retrieval makes sense—forexample, if there are security rules that you must implement. However, in general, thesimple recommended approach is to treat data retrieval like a crosscutting concern (that is,an aspect), using a RetrievalFactory, for example.

Page 11: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

11/12msdn.microsoft.com/en-us/library/cc984279.aspx

Figure 11 shows the same diagram as previously, now including the RetrievalFactory andother standard aspects:

Figure 11. Layer structure, showing RetrievalFactory and other aspects

The RetrievalFactory is highlighted here simply to draw attention to it. The Service Locatorpattern is not required, although it could be involved if you were to use it to abstract awayfrom your persisters.

The RetrievalFactory returns a factory (surprise!) for retrieving the particular type of entitiesthat you are looking for, usually with something likeRetrievalFactory.GetFactory(typeof(Student)) orRetrievalFactory<Student>.GetFactory() or however else you might address it. TheRetrievalFactory is allowed to call persisters directly, instead of passing-through the twosteps of an action and executor itself, because there are no business rules that usually areassociated with retrieval at all, or any transactions. If there are, you need both an action andan executor.

The other thing that having the RetrievalFactory does is truly separate the reading use of theinformation from the writing use of the information, which sometimes is helpful, as insituations in which different user accounts should be used for security purposes or in whichyou want to have some centralized caching.

Regarding TransactionScope

The TransactionScope class offers the ability in .NET Framework 2.0 and later to declare atransaction to which other objects can attach themselves, without having to describe rightupfront what type of transaction it is. In .NET Framework 2.0, it required Microsoft SQLServer 2005 to be the database, for it to be a database transaction; otherwise, it waspromoted to a DTC transaction, which can cause security concerns (a Swiss-cheese firewall).

From a model perspective, there are two places in which the TransactionScope class couldfit. One place is the action class’s Execute method, before invoking the executor. The benefitof this approach is that it would allow for composite actions—for example, Action X = Action Y+ Action Z, where the Execute method of Action X would create a transaction and then callthe Execute methods of Action Y and Action Z, and where each Execute method wouldcheck to see if there was a transaction to subscribe (otherwise, it would create its own).

The other logical place to think about using the TransactionScope class is within theexecutor, which would declare a transaction (and the persisters would join in), without having

Page 12: The Action_Executor Pattern

5/13/12 The Action/Executor Pattern

12/12msdn.microsoft.com/en-us/library/cc984279.aspx

© 2012 Microsoft. All rights reserved.

to have the transaction information passed as a parameter to key methods.

A Quick Note on Web Services

A benefit of using this approach is that, when you must change the presentation layer from,say, a Web site to Web services, having pushed all of the logic into well-organized actionsand entities, the business layer is ready for it.

A Quick Note on DAL Generators

On occasion, upon discussing this model, someone will bring up that a DAL generator—be itCodeSmith with specific templates, or MyGeneration or SubSonic—addresses the entire need,which is incorrect. It produces persisters; but, again, it does not address the responsibilitiesthat are taken on by the executor—instead, pushing this into the action class. Remember thatthe executor is the logical place in which to put those extra stored-procedure calls or otheractivities that must be made—the logic that is in alignment with the action being performed,but not warranting an entity of its own.

Conclusion

The Action/Executor pattern identifies a strategy for mapping use cases to code, allowingbetter visibility and agility, and addresses the sticky issues of contaminating entities andskipping over proper use of transactions (as it is unclear where they go).

This pattern does not fit all types of projects; however, it does address a significant numberof them. More importantly, for the ones that it does not fit, it brings up the notion ofidentifying areas of responsibility within each layer.

The Action/Executor pattern has been adopted by several companies as their default .NETapplication-design pattern. Their support in concretizing the pattern over the past two yearshas been greatly appreciated.

Did you find this helpful? Yes No