19 April 2013

Pub/Sub Implementation II – The Message Box

Okay, I call mine "Broker" but MessageBox is more correct- therefore it will be "MessageBox" from here on out.

The MessageBox is just defining the connection to the Database.  Others would call it a DAL, but I'm not actually accessing Object Data, I'm accessing messages.  Thus, "MessageBox."  In my case, I chose RavenDB for this implementation.  I really like RavenDB- it's lightweight, it's intuitive, and it's fast to get up-and-running.

Here's my implementation:

  private MessageBox()
  {
    store = new DocumentStore() {Url = http://localhost:8080};
  }
 
  private static Lazy<MessageBox> instance = 
      new Lazy<MessageBox>(() => new MessageBox());
  public static MessageBox Instance 
      {get{return instance.Value;}}
 
  public bool Insert<T>(T item, string db)
  {
    using(var session = Store.OpenSession(db))
    {
      session.Store(item);
      session.SaveChanges();
    }
    return true;
  }  
 
  public bool BulkInsert<T>(IEnumerable<T> items, string db)
  {
    using (var bulk = Store.BulkInsert(db))
    {
      for(int i = 0; i < items.count(); i++)
      {
        bulk.Store(items.ElementAt(i));
      }
    }
    return true;
  }
 
  public T GetItemById<T>(string db, string id)
  {
    using(var session = Store.OpenSession(db))
    {
      return session.Load<T>(id);
    }
  }
 
  pulic IEnumerable<T> GetItemsByIds<T>(string db, string[] ids)
  {
    using(var session = Store.OpenSession(db))
    {
      return session.Load<T>(ids);
    } 
  }
 
  public IEnumerable GetPagedMessages<T>(string db, int start, int pageSize)
  {
    using(var session = Store.OpenSession(db))
    {
      var result = session.Query<T>()
           .Skip(start)
           .Take(pageSize);
      return result.ToList();
    }
  }
}

Okay, that was a beating, so let's look at what the code actually does.  First off, we set up our DocumentStore (that's the connection to the actual RavenDB database. 

Next, we set up the MessageBox as a singleton.  We'll probably be calling it a lot, and it's better if we don't call multiple copies per app-domain.

Finally, we establish the actual operations the MessageBox will do for us.  Note that this is the MessageBox code.  If we were using RavenDb as our backing store for our business objects, we'd probably have a different class (or just have every application use the RavenDB client, and write their own code).

There are two kind of tricky methods here.  The first is BulkInsert- I'm not sure why RavenDB doesn't support a Save method (even under it's "BulkInsert" functionality) that allows me to pass an array or enumerable of objects, but it doesn't.  So we use BulkInsert (which still only takes one at a time??).  The reason we're using a For loop, instead of ForEach, is that using BulkInsert to save each document automatically provides that object with an Id- thus modifying our collection- thus making ForEach unavailable.

The second one is the GetPagedMessages.  RavenDB, by default, will only provide you with 128 messages at a time.  So, if you expect more than that, you have to page through the results.  This is just a basic paging system, there are actually better ways to do it with RavenDb, but a) this is fast, b) this is functional, and c) it isn't actually wrong.  I use ToList() there, because the session.Query() method returns a IRavenQueryable collection, and sometimes that doesn't play nice with IEnumerable.  Using .ToList() fixes that.

Okay, that was a slog.  But I promised the MessageTypes, so here they are:

    public class NewOrderMessage : Message
    {
        public string OrderId { get; set; }
        public string CustomerId { get; set; }
        public decimal OrderTotal { get; set; }
        public Dictionary<string, int> OrderItems { get; set; } //TKey = SKU, TVal = quantity
    }
 
    public class BilledOrderMessage : Message
    {
        public string OriginId { get; set; }
        public string OrderId { get; set; }
        public string CustomerId { get; set; }
        public bool Approved { get; set; }
    }
 
    public class AllocatedOrderMessage : Message
    {
        public string OriginId { get; set; }
        public string OrderId { get; set; }
        public Dictionary<string, int> OrderItems { get; set; }
        public string WarehouseId { get; set; }
        public bool Allocated { get; set; }
    }
 
    public class FulfillmentRequestOrderMessage : Message
    {
        public string OriginId { get; set; }
        public string OrderId {get; set;}
        public string BillingId { get; set; }
        public string AllocId { get; set; }
        public string WarehouseId { get; set; }
    }
 
    public class FulfilledOrderMessage : Message
    {
        public string OriginId { get; set; }
        public string OrderId { get; set; }
        public string RequestId { get; set; }
        public bool Fulfilled { get; set; }
    }

 

Next Up- The Order Management System.

No comments:

Post a Comment