Our problem: We were designing a system which would provide live stock data from various stock exchanges to various clients. We decided to use Martin Fowler's event sourcing pattern to help with our distributed architecture. Greg Young's articles and presentations on practical implementations on this pattern has helped a lot as well.
What we ended up with was a "Server" (Gregg young would call this the database) domain and a "Client" domain. I'm not going into detail on why we made this decision but our problem was that the Client domain was very similar to the Server domain. Actually the Client domain was a subset of the Server domain.
Both the Server and Client domain had entities: MarketDepth which contains Bids and Offers. The major difference was the behavior associated with inserting and removing bids from MarketDepth. The server domain receives raw data from the stock exchanges and translates those messages into domain specific events. Only the required domain events (like BidsChanged, OffersChanged, TradesChanged, etc) get routed to the client domain because that are the only events the client domain are interested in.
So the problem is that both the server and client domain have the same entities but the behavior around them (input and output of events) are different. We decided to use the decorator pattern to solve this problem. This pattern wraps a class around the entity and associates behavior to the entity. This allows you to have multiple decorators but only one entity.
An Example:
As discussed before, we have a MarketDepth entity:
public class BidDTO{
public BidDTO(string symbol, int price)
{
Symbol = symbol;
Price = price;
}
public string Symbol { get; private set; }
public int Price { get; private set; }
}
public class MarketDepth
{
private readonly IList<BidDTO> _Bids;
public MarketDepth()
{
_Bids = new List<BidDTO>();
}
public bool SaveBid(BidDTO bid)
{
if (!_Bids.Contains(bid))
{
_Bids.Add(bid);
return true;
}
return false;
}
}
Next I declare a base decorator class that I will use. As you can see it is an abstract class with an abstract method (which will be entry point for saving a bid) and it contains a protected MarketDepth variable.
public abstract class Decorator{
protected MarketDepth _MarketDepth;
public Decorator(MarketDepth marketDepth)
{
_MarketDepth = marketDepth;
}
public abstract void HandleBidEvent(object e);
}
I have two events defined: one for raw data coming into my Server domain and a domain event that Server domain raises and Client domain will receive:
public class ServerEvent{
public ServerEvent(string rawMessage)
{
RawMessage = rawMessage;
}
public string RawMessage { get; private set; }
}
public class BidChangedEvent
{
public BidChangedEvent(BidDTO bidDTO)
{
Bid = bidDTO;
}
public BidDTO Bid { get; private set; }
}
Next I have the ServerDecorator. This decorator will be used at the Server domain to save MarketDepth data based on messages received from the stock exchange:
public class ServerDecorator : Decorator{
public ServerDecorator()
: base(new MarketDepth())
{
}
public override void HandleBidEvent(object e)
{
var serverEvent = (ServerEvent)e;
BidDTO bid = GetBidFromMessage(serverEvent.RawMessage);
if (_MarketDepth.SaveBid(bid))
{
//raise outgoing event
BidChangedEvent domainEvent = new BidChangedEvent(bid);
//raise domain event
}
}
private static BidDTO GetBidFromMessage(string message)
{
string symbol = message.Substring(0, 4);
int price = Convert.ToInt32(message.Substring(4, 4));
return new BidDTO(symbol, price);
}
}
And lastly, the ClientDecorator that will be used to listen for domain events from the server and apply those events to the MarketDepth on the client domain:
public class ClientDecorator : Decorator{
public ClientDecorator()
: base(new MarketDepth())
{
}
public override void HandleBidEvent(object e)
{
var domainEvent = (BidChangedEvent)e;
if (_MarketDepth.SaveBid(domainEvent.Bid))
{
//raise another event to client user
}
}
}
As you can see, this makes the code re-usable and clearly separates the responsibilities of the various classes. Hopefully this will be useful for someone in the future. Feel free to pop me an email or leave a comment if you would like the source code or more explanation on the pattern and how it is used.
Till next time.

No comments:
Post a Comment