Geeks With Blogs
Boy Meets 'Hello World' Blogging the journey from College Grad to .NET Developer

 

UPDATE (6/20/08):

If you are at all confused about some of the techniques outlined in the blog posts by Udi and Ayende I discuss below, specifically about their multithreading-capabilities, you might want to check out my future post here.

---

 

One of the first issues I have already found myself having to deal with in my game is the idea of how messages will be sent "from the entity" (since the entity is ultimately in charge of when something happens, and thus must trigger a message being sent), without needing the entity to know about how to send messages. This post describes some of the strategies I've been considering.

The example I will use is the UnitLocation entity, which stores where the unit is,  receive MoveUnit messages, and check to determine if the unit can move there (it must be a valid location on the map, and only one plot away from the unit's current location). If it can, it will do so, and send out a UnitMoved message. Otherwise, it will send out a UnitMoveFailed message, with a string for why it cannot. Keep in mind, there are other things that this entity must do, namely handle the map being initialized through a MapInitialized message and the unit being started through the UnitSpawned message, but for starters I'm just going to assume a 9x9 map with the unit in the middle, and that that unit is the only unit in the game. This all initialized when the system starts.

So, my entity could look something like this...

   1:  public class UnitLocation
   2:  {
   3:      private readonly Map map;
   4:      private MapCoordinate currentCoordinate;
   5:   
   6:      public UnitLocation()
   7:      {
   8:          map = new Map(new MapDimensions(9, 9));
   9:          currentCoordinate = new MapCoordinate(4, 4);
  10:      }
  11:   
  12:      public void MoveUnit(MapCoordinate newCoordinate)
  13:      {
  14:          CheckValidCoordinate(newCoordinate);
  15:          CheckAdjacent(newCoordinate);
  16:   
  17:          // If the above pass...
  18:          currentCoordinate = newCoordinate;
  19:          // Send UnitMoved message with newCoordinate.
  20:      }
  21:   
  22:      private void CheckValidCoordinate(MapCoordinate newCoordinate)
  23:      {
  24:          if (map.IsValidCoordinate(newCoordinate) == false)
  25:          {
  26:              // Send UnitMoveFailed message with reason "Invalid Coordinate"
  27:          }
  28:      }
  29:   
  30:      private void CheckAdjacent(MapCoordinate newCoordinate)
  31:      {
  32:          if (map.AreCoordinatesAdjacent(currentCoordinate, newCoordinate) == false)
  33:          {
  34:              // Send UnitMoveFailed message with reason "Coordinate not adjacent"
  35:          }
  36:      }
  37:  }

So, here are my criteria for choosing a strategy that allows the entity to notify the service-level code that is utilizing it (henceforth, the "Gateway" to the entity) that it should be reacting to events. I Will be be constantly referring to when the entity wants a message sent to triggering an "event" that the gateway responds to. Keep in mind, though, that this does not mean that I must use C# events.

Criteria:

1.) Strategy must be easily testable. More than just being easy to unit test, an easily testable strategy will also determine how easy it is for the Gateway to be able to do it's job. If I have to hook up and intercept a bunch of interface calls in each test, then I'll more than likely be needing to wire up a bunch of interfaces in my code.

2.) Strategy must not assume what message a method will be sending, nor how many messages it is sending. As shown above, a method will not necessarily trigger one type of event per method. While there isn't any method listed in my example that shows sending multiple messages, or no messages at all, I would still like to support those cases.

3.) Limit any changes to entity return-values & parameters. I can deal with small, inconsequential changes, but don't make every method need to use three out parameters to work.

4.) Keep strong encapsulation. I prefer not to expose my state. I will not accept strategies that require the above example to use getters for the map, current location, or any other added additional member variables.

5.) Allow Gateway to control message results. I want the Gateway to be able to easily do what needs to be done with the messages sent from the entity. In other words; the entity cannot bypass the gateway. For example, I might want to have the messages the entity sends out to be wrapped with some sort of envelope, or aggregated together in one super-message. The gateway will most likely be dealing with these concerns, so I want it to get first crack at the results of any messages that are sent out. If it decides it just wants to send it directly out, it can have the option of doing so.

 

With these criteria in mind, here are the strategies I considered, in no particular order...

 

List of Strategies:

1.) Static Send.Message

The entity creates the message, and sends it to a Send.Message method in the sky. Testing can be done assuming that whatever the Send.Message method does can be faked out. This can be slightly changes to have more than one static method, one for each message, but that just seems a ton of work.

   1:  [Test]
   2:  public void SendsSomeMessage()
   3:  {
   4:      MessageInterceptor interceptor = new MessageInterceptor();
   5:   
   6:      using (Send.RegisterIntercept(interceptor))
   7:      {
   8:          // Exercise entity
   9:          // ...
  10:   
  11:          var expectedMessage = //...
  12:          Assert.That(interceptor.Intercepted(expectedMessage));
  13:      }
  14:  }

 

Of course, this can be thrown right out. It's the simplest solution, in my opinion, but not only might the entity need to know the message data structure, it also will bypass the gateway if the gateway doesn't "hook into" the message sending system.

2.) Passing a Message-Sending object into entity through constructor/property injection, or method parameters.

Injection of anything into entities seems to give people tummy aches, but I've never been one to shoot down an idea just because I ate too many Oreos (except, perhaps, the idea of eating more Oreos). In this idea, an object implemented in the service-layer will send the message is injected once into the entity. This can be mocked more easily than using the static Send.Message.

In both the constructor and property injection, mind you, the object need not be done by an inversion of control container. It could be as simple as...

   1:  public class UnitLocationGateway: Handles<MoveUnitMessage>
   2:  {
   3:      private readonly Bus bus;
   4:      private readonly UnitLocation unitLocation;
   5:   
   6:      public UnitLocationGateway(Bus bus)
   7:      {
   8:          this.bus = bus;
   9:   
  10:          var unitLocationMessageSending = new UnitLocationMessageSending(bus);
  11:          unitLocation = new UnitLocation(unitLocationMessageSending);
  12:      }
  13:          
  14:      public void Handle(MoveUnitMessage message)
  15:      {
  16:          unitLocation.MoveUnit(new MapCoordinate(message.X, message.Y));
  17:      }
  18:  }

 

At first, the problem with this code is that, in terms of unit testing, it's not as testable as I would LIKE it to be. That is to say, I would need to have some way of determining that the UnitLocation was created with the correct service type. Assuming I implement "constructor equality" on UnitLocationMessageSendingService, I could do this by having MoveUnitGateway take a UnitLocation factory service, that I would be able to provide a mock unit location for. I don't like creating new services just to help facilitate unit testing, I will make an exception for this case, as it typically alleviates all my other pains. Besides, I would never write a unit test for this class...

   1:  public class UnitLocationBuilder
   2:  {
   3:      public void BuilderUnitLocation(UnitLocationEventHandler messageSending)
   4:      {
   5:          return new UnitLocation(messageSending);
   6:      }
   7:  }

 

All this class earns me is the need to change the parameters whenever the UnitLocation constructor parameters change. I'll take that hit for the unit testing abilities. BTW, I keep talking about passing a UnitLocationEventHanlder, which is probably an interface that consists of a method for each type of event that might happen in the entity. I would probably separate this into an interface per event, just to make it easier to deal with being able to handle different events with different objects.

So this can work, unless I want to change what type of object I'm using as my message-sender (for example, replace it with an "IgnoreEvents" object that just drops the messages on the floor, which I guess is debatable as to whether it would be of any use). Another failure is that any destruction of the UnitLocation at any time, such as saving it to some repository, and then destroying the object (the typical lifecycle I deal with in my web projects, not sure how much I'll see it writing this game), will mean that at reconstitution, I most likely won't have my message-sending objects anymore. Finally, and most importantly to me, it uglies up my constructor.

Property injection fixes some of these issues (you can "rewire" the services after a reconstitution of an entity from the repository) but isn't testable. For it to be testable, I would have to find some way of determining that I passed the correct service objects to the properties, which would require getters/other means on those properties (yuck) or equality overriding. I don't mind doing "constructor equality", because I'm assuming that the objects passed into the constructor will not be changed, making Equals (and thus, GetHashCode()) less likely to cause disappearing acts in dictionaries. However, using properties for your equals overrides, you are almost guaranteeing that they will be changed. Of course, I could make all of this easier by just allowing myself to use getters, but I guess I'm a masochist like that.

Finally, you can do all this "injection" through the method calls as argument. Most problems are solved, but now you are uglying up you method parameters. If you are like me and want your message sending objects to be separate interfaces as opposed to all lumped into one, this will ugly up your parameters even more.

3.) Make a static event or delegate for the Gateway to subscribe to.

These ideas came from posts called "How to create fully encapsulated Domain Models" and "Re: How to create fully encapsulated Domain Models", by Udi Dahan and Oren Eini, respectively. If you are interested in the concept of encapsulation of your domain from the service layer without injection, you should definitely check out the posts and comments. The idea is to create some outside system with which the gateway subscribes to and uses to receive event triggers from the entity. In Udi's strategy, you use C# events on a static class. In Oren's, you use delegates instead (still on a static class). The use of events vs. delegates or events with weak references isn't as important to me

4.) Make events/delegates on the entity for the Gateway to subscribe to.

Basically, the same thing as above, but rather than deal with a completely separate class, you place these directly onto your entity. You get all the advantages/disadvantages of above, but you don't need this separate class. Unfortunately, you also get this complexity that Udi mentioned:

The problem is that if an event is raised by a child (or grandchild object), the service layer object may not even know that that grandchild was involved and, as such, would not have subscribed to that event. The only way the service layer could work was by knowing how the Domain Model worked internally - in essence, breaking encapsulation.

Of course, you could fix this by having any event on the child also be an event on the aggregate root, where the child event invocation triggers the aggregate root invocation. For this, you would have to wire up the children's event to the entity's, which is possible, but a pain. Also, assume that you have an aggregate root with a child entity. In this case, we have three classes that know about the event and need to have members that do something about it: The gateway, the aggregate root, and the child entity. In the static class, we have the same number of classes that need to know about this event: the gateway, the child, and the static class. So far, it's the same. Now, assume that child entity has a GRANDCHILD which triggers an event. Here, four classes need to "know" about the event, the gateway, aggregate root, child, grandchild. In the static class system, you still only have three classes that know about it. I don't think that wiring up and passing these events through the entity "chain" would fall under the category of "breaking encapsulation" any more than the aggregate root returning results from a getter on the child would, but I do think that this method will result in more code, and not in a good way.

Finally, if for any reason you wanted to notify the gateway of an event during entity construction, it would be impossible, since the gateway couldn't wire itself to the delegate before the entity was created. This could easily be fixed (and would probably be a better idea than having a more complicated constructor) by using a builder for the entity, and making the builder send the event notification.

5.) Have method return an "OperationResult" object.

Although Oren used the above strategy in his post, he saw it more of how he would "approach Udi's sample", posting in his comments, "I might go with an OperationResult instead, but it is basically the same idea." Edit: Oren wasn't specific about this, this is simply what I would do.

The nice part about this strategy is that, assuming that you have stuck with Command/Query Separation, your methods that "do things" should all have void return types. All other methods and properties should not be triggering events, as they're just queries, and so you don't need to worry about how to return an OperationResult from them. Of course, you could also say that all of a sudden your commands are returning something, violating Command/Query Separation. This is something I'm willing to do, however, and I guess is really a judgement call to the same degree that strict following of CQS is in the first place.

Now, you might argue that even getters or other methods that don't run side effects could trigger events. Since these members already have return types, how could we return an OperationResult as well? What if we have an event that is produced every time that the a piece of data is retrieved? Maybe you want to keep a tally of times a report is generated (for domain-related ideas, not for performance profiling). To this, I say that this "tracking" is an outside concern. If this needed to be done, then another service could receive the PullReport message as well, and handle that logic. There is no reason for a query member to be triggering events.

Gateway code is kept simple (no need to subscribe to any events). The question is what does the OperationResult object look like? Consider the following example: Suppose I have a domain object Visibility that, on receiving a UnitMoved message, determines if any new plots are visible to the owner of the unit. The operation result object (and thus the type that the method returns) could look like this...

   1:  public class UpdatePlayerVisibilityOperationResult
   2:  {
   3:      private readonly IList<MapCoordinate> mapCoordinatesRevealed;
   4:   
   5:      public UpdatePlayerVisibilityOperationResult(IList<MapCoordinate> mapCoordinatesRevealed)
   6:      {
   7:          this.mapCoordinatesRevealed = mapCoordinatesRevealed;
   8:      }
   9:   
  10:      public IList<MapCoordinate> MapCoordinatesRevealed
  11:      {
  12:          get { return mapCoordinatesRevealed; }
  13:      }
  14:  }
 

The problem I have with this is that if the visibility operation needs to start sending out additional messages (like in the case of my MoveUnit, there are failure cases that could happen as well), I need to keep adding on new data to this object for each scenario. That's not just a violation of OCP, it's just plain annoying.

Instead, I would probably have an OperationResult object, and each different type of result (UnitMoved, UnitMoveFailed, etc.) gets it's own type(which will probably just be a struct with the data). They add each event to this result object, and return the collection to the gateway. The gateway tells the operation result object how to handle each type of event by giving it objects of it's own, one for each type of event it wants to handle. Here is how I would like the class to work...

   1:  public class UnitLocationGateway : Handles<MoveUnitMessage>
   2:  {
   3:      private readonly Bus bus;
   4:      private readonly UnitLocation unitLocation = new UnitLocation();
   5:   
   6:      public UnitLocationGateway(Bus bus)
   7:      {
   8:          this.bus = bus;
   9:      }
  10:   
  11:      public void Handle(MoveUnitMessage message)
  12:      {
  13:          var newCoordinates = message.MapCoordinates;
  14:   
  15:          var unitMovementFailed = new SendUnitMovementFailedMessage(bus);
  16:          var unitMoved = new SendUnitMovedMessage(bus);
  17:   
  18:          var results = unitLocation.MoveUnit(newCoordinates);
  19:          results.UseHandler<UnitMovementFailed>(unitMovementFailed);
  20:          results.UseHandler<UnitMoved>(unitMoved);
  21:      }
  22:  }

 

You can see lines 15 + 16 creating my event handler objects. This means my gateway will just need to mock out the OperationResult returned from the entity, and assert that I'm getting the correct handler objects passed into it during the UseHandler method calls. Finally, because I'm using a reusable OperationResult object, I can create a really sweet OperationResultMock object, which I've always found better than dealing with mock frameworks.

By the way, the generic call on UseHandler can be made implicit, but I placed them there to show how the UseHandler method is working. It will ensure that the object passed is of a type EventHandler<T>. Of course, if I found making all these handler objects too much, I could just as easily change the arguments of UseHandler to allow for an Action<T>. But, every time I implement new functionality by creating a new class, I get the feeling I'm doing something right, so creating new classes isn't that big of a deal to me. :P

Finals Thoughts:

As you can probably tell, I've really been leaning towards using the OperationResult. Interestingly, I will no doubt find that my UnitMoved event struct will look remarkably similar to my UnitMovedMessage struct, which immediately makes me question the validity of attempting this whole ruse of encapsulation in the first place. I'm sure this is the same thoughts that run through the head of some people when they wonder if they really need that service tier on their web-app, or if they can just put the repository and entities right on their MVC controller. The difference is that the domain is a mystical, magical beast, and that putting infrastructure in your infrastructure (service-layer code in your MVC controller) if ok, while putting infrastructure in your domain (even though they look REMARKABLY similar) is "bad practice". However, for now I'm going to trust the wisdom of the ancients and not dwell on this too much. After all, one of the things I've been trying to get out of my head is that the goal of OOP is NOT to reduce duplication, as was told to me time and time again in college.

Hopefully, I will be able to come back with thoughts on how the OperationResult strategy has treated me. Also, hopefully my posting of this (as opposed to typing some, thinking some, sleeping some, changing my mind, typing some, eating, changing my mind) will get me to start working, and stop over-thinking.

Posted on Wednesday, June 4, 2008 4:14 AM TheGame , Design | Back to top


Comments on this post: Encapsulating architecture from domain entities.

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © mhildreth | Powered by: GeeksWithBlogs.net