Thursday, February 10, 2011

Ellemy.CQRS goes to the cloud, The Amazon SNS Event Publisher

So, long story short, I left my old job. Starting a new one with a very exciting company. The architect there apparently LOVES Amazon services. So, I figured I’d go write a quick Event publisher for Ellemy.CQRS using Amazon Simple Notification Service. Won’t hurt to be familiar with it, and sounds kinda cool.

First of all, I signed up for the service,  it’s essentially free. Yes, they want a CC card, but they charge nothing as long as you don’t exceed a gig a month, and it’s $0.19 per gig after that. I’m not gunna even approach a gig, so I’m fine.

First of all, I went and configured a Topic, If you’re an NServiceBus guy (like me), a Topic is essentially a Queue – someone feel free to correct me there, but that’s how I think of it.

You can make API calls all day to administer topics, but that’s not the point of this blog, the point of this blog is to send a message out to the cloud. So I’m gunna get a topic created as quick as possible.  Amazon made a nice little web interface to do that.

I logged in with my Amazon Account and added a topic, really simple.

createtopic

I named my topic, and got redirected to a nice little screen that gave me the Topic ARN. We’ll need that along with the Key Id, and Secret. You can add subscribers from the next page, and one of the options is Email (JSON). That’s pretty cool. I just select that. Easy way to check if the message is publishing ok.

Let’s get in some code now….. We need to implement a new version of IEventPublisher.  Here’s what it looks like.

   1:  namespace Ellemy.CQRS.Event
   2:  {
   3:      public interface IEventPublisher
   4:      {
   5:          void Publish<TDomainEvent>(TDomainEvent @event) where TDomainEvent : IDomainEvent;
   6:      }
   7:  }

We’re just gunna send the event off to the cloud there.


I can hand roll GET/POST/PUT all day, but I really don’t feel like doing all the plumbing so I downloaded the Amazon AWS .NET SDK.  It’s got a nice wrapper around all that stuff. Spun up the object browser and looked around and saw this puppy.


objectbrowser


I think that’s exactly what we need.  Let’s just do the simpliest thing first and new one of those up in the publisher. I’ll clean up in a second.


   1:  public class AmazonPublisher : IEventPublisher
   2:      {
   3:          public AmazonPublisher()
   4:          {}
   5:   
   6:          public void Publish<TDomainEvent>(TDomainEvent @event) where TDomainEvent : IDomainEvent
   7:          {
   8:              var serializer = new JavaScriptSerializer();
   9:              var payload = serializer.Serialize(@event);
  10:              var client = new AmazonSimpleNotificationServiceClient("myAccessId",
  11:                                                                     "mysecretKey");
  12:   
  13:              var request = new PublishRequest
  14:                                {
  15:                                    Message = payload,
  16:                                    Subject = @event.GetType().AssemblyQualifiedName,
  17:                                    TopicArn = "arn:aws:sns:us-east-1:451419498740:EventMessage"
  18:                                };
  19:              client.Publish(request);
  20:   
  21:   
  22:   
  23:          }
  24:   
  25:          
  26:      }

Lets go tell Ellemy.CQRS to use it. We’ll make a pretty Extension method in a bit, for now, lets do it ugly.


   1:  Configure.With()
   2:                  .StructureMapBuilder(ObjectFactory.Container)
   3:                  .CommandExecutorsAreInAssemblyContainingType<CreateMessage>()
   4:                  .HandlersAreInAssemblyContainingType<MessageReadModel>()
   5:                  //.NServiceBusPublisher(ObjectFactory.Container.GetInstance<IBus>());
   6:                  .PublishEventsWith(new AmazonPublisher());

Let’s go fire it ok, here goes nuthin!


BAM!


message


How freekin awesome is that?


A little cleanup now…. First lets make a extensions class to make it so people don’t have to new up that AmazonPublisher object, and let them send in the config stuff. I’ve kinda blogged on how I like doing this before, but what the heck? I’ll do it again…


First of all, I’ll create a little fluent config to allow users to configure their amazon KeyId, Secret and TopicArn. In a future release I’m gunna create a Factory to resolve TopicArn by domain event type, but for now, let’s stick to a single TopicArn.


Here’s what I cooked up.


   1:   public class AmazonPublisherConfig
   2:      {
   3:          private readonly Configuration _configuration;
   4:   
   5:          public AmazonPublisherConfig(Configuration configuration)
   6:          {
   7:              _configuration = configuration;
   8:          }
   9:   
  10:   
  11:          internal string AccessKeyId { get; private set; }
  12:          internal string SecretKey { get; private set; }
  13:          internal string TopicAccessResourceName { get; private set; }
  14:   
  15:          public AmazonPublisherConfig AwsAccessKeyId(string accessKeyId)
  16:          {
  17:              AccessKeyId = accessKeyId;
  18:              return this;
  19:          }
  20:   
  21:          public AmazonPublisherConfig AwsSecretKey(string awsSecretKey)
  22:          {
  23:              SecretKey = awsSecretKey;
  24:              return this;
  25:          }
  26:   
  27:          public AmazonPublisherConfig TopicArn(string value)
  28:          {
  29:              TopicAccessResourceName = value;
  30:              return this;
  31:          }
  32:          public Configuration CreatePublisher()
  33:          {
  34:              _configuration.PublishEventsWith(new AmazonPublisher(this));
  35:              return _configuration;
  36:          }
  37:      }

At some point I’ll let config files do this, but for now, lets keep it simple.  Basically, we just build up the config values fluently, and call CreatePublisher and that’ll set the IDomainEventPublisher to the AmazonPublisher. Now lets just clean up the AmazonPublisher.


   1:   public class AmazonPublisher : IEventPublisher
   2:      {
   3:          private readonly AmazonSimpleNotificationServiceClient _client;
   4:          private readonly string _topicArn;
   5:   
   6:          internal AmazonPublisher(AmazonPublisherConfig config)
   7:          {
   8:              _client = new AmazonSimpleNotificationServiceClient(config.AccessKeyId, config.SecretKey);
   9:              _topicArn = config.TopicAccessResourceName;
  10:          }
  11:         public void Publish<TDomainEvent>(TDomainEvent @event) where TDomainEvent : IDomainEvent
  12:          {
  13:              var serializer = new JavaScriptSerializer();
  14:              var payload = serializer.Serialize(@event);
  15:              
  16:              var request = new PublishRequest
  17:                                {
  18:                                    Message = payload,
  19:                                    Subject = @event.GetType().Name,
  20:                                    TopicArn = _topicArn
  21:                                };
  22:              _client.Publish(request);
  23:          }
  24:      }

Nice and simple, huh? I’m setting the Subject to the event type name right now, and we’ll create a subscriber soon that’ll use that to go find the IDomainEventHandlers for type with the name that matches the subject. Not sure if that’ll work or not, but we’ll see soon enough huh? I’m just JSON serializing the Event. I think that’ll work great.


Lets fire her up again, and see. Hit the UI, fire off a message, and here’s what I get (well, after I took an eraser to the signature so you guys don’t see how many terabytes of messages you can send up to the cloud and charge up my credit card).


hellofromthecloud


I’m having a blast working on this stuff, and I see that I’m getting a few watches on github, Folks, please leave comments with what you’d like to see added to it, or any other blog requests.


Have fun with it!

2 comments:

  1. Hey Elliot,

    I'm enjoying reading your posts on CQRS here, keep them coming please.

    Ben

    ReplyDelete
  2. I am a happy Amazon customer since month 1 and the service has been superb,no complaints. Amazon SNS brings the high tech toys to small shops and developers that can benefit me without worrying about big budgets.Bucket Explorer Amazon tool really help me to handle SNS service for me on Amazon....

    ReplyDelete