Some Background

When the Halo 5 API first came out, I knew I wanted to do something big with it. I've been a part of the Halo user generated content scene since it started on consoles with Halo 3 and Forge in 2007. So I have a passion for the level design community and I knew that with my recent architecture experience I could whip up something that could be awesome for the community.

The Cartographer API [CAPI] is the result of these passions of mine. CAPI is an extension of the Halo 5 API that seeks to provide analytics on user generated content by aggregating data across the entire Halo 5 custom games community. I made my first commit on March 31st, 2016... and 2 months later, yesterday I pushed CAPI to an Alpha state for the Halo API Hackathon. The goal of this API is to provide developers, communities, and content creators with data to enhance the generation of content. It allows people to find games that have been played on your content, and find the people that have played on your content. Data like weapon usage, kill times, event data, player reach, etc. is made possible for maps/gametypes/creators as a whole not just single matches or players. Today we have some of that functionality, but I still have a ways to go to add features that are valuable to the community. But it is made easier thanks to the AWS stack that I'm working with.

The Leap to Serverless

So initially I was planning on just dabbling in Lambda and using EC2 as the core, using a Microservice architecture and hosting the APIs there on a per domain basis. Especially after I had just gotten done with my contract at Nike and my mind was filled with Microservices. But the combination of API Gateway and Lambda, especially after last year's Re:Invent had me intrigued. The thing that got me most excited was seeing the talk on Practical DynamoDB with Java which used Lambda. So I took some time to play with it... and boy was it fun. It was like going back to school and easily only coding in `public static void main(String[] args)`.

I haven't taken enough time with Node or Python yet, so Java being a possibility meant everything to me since Nike loves some Java. So my skills were fresh and ready for use. I know that Java is slower with Lambda, but it isn't too bad since I was able to cut each function down to 7 MB rather than the initial 30 MB each using Maven. Anyway, Java in Eclipse with the AWS Toolkit and Lambda made coding a cinch. I was sick of the hassle of deploying an AMI, managing instances, scaling, etc. Lambda made that easy. And not having to manage the systems details of running a Cassandra DB by switching over to DynamoDB was heaven. Cloud services is life folks... learn it, live it, love it.

... and then Nanoservices

So we've got our desired tech stack. But what about our business logic architecture? Well Microservices are becoming the mainstream and that's what we did at Nike. I mean I saw a tweet where a team went from initial commit to production in just an hour. That's amazing! The biggest problem I found with Microservices though... is many people differ on their thoughts on how big "Micro" really is. And the amount of boiler plate that was involved in the infrastructure and standards just felt to bloated in the Microservices I had experience with... mainly because it seems people tend to shy away from refactoring out and centralizing code and would rather copy and paste. Yeah that doesn't make maintenance easy at all. I mean I copy-paste for expediency, but then you quickly turn around and refactor what you can for re-usability right?

And then there were Nanoservices with Lambda and I felt my world made sense again. Thanks to Lambda and other cloud function services, Nanoservices are no longer an anti-pattern. That is a blessing and it fixed my issue with Microservices. For me, I've defined a Nanoservice as a single action or responsibility. Following the SOLID principles, I follow the rule "if you ever have to say this service does this 'AND' this... then you can probably split it up into a new service." That made the size of a service much more objective for me personally. So Nanoservices was the way to go. Some people are stilling using their Lambda functions as controllers for a Microservice and honestly that feels like a waste to me. What does this buy CAPI?

Sticking close to "Free" with big data

One of the major things you hear about when people pitch Lambda to you is cost. Lambda is something like 4x cheaper than your standard EC2 application. I always dreaded paying for uptime with an EC2 instance that wasn't even being used. And sure auto-scaling exists, but having to figure out instance size and amount is a headache. Thank you Lambda for taking that away. Not only that, Lambda has a tier that is ALWAYS free even without the free trial account. Under 1 million requests and 400,000 GB/sec in execution time and you are free. Even after that every 400K time is like $6 and 1 million requests is an additional 50 cents.

The other services I decided to tack on also provide free tiers. API Gateway for under 1 million calls, SNS for 1 million requests, SQS for 1 million requests, DynamoDB for 25 GB of data and 25 each of Read/Write capacity. There is your tech stack right there and all you really need. Some people will throw in some s3 for serverless architecture, but I haven't dabbled enough there yet. On top of that CloudFormation is free infrastructure as code. What more could you want? And with Nanoservices being completely segregated and more fine-grained I have more control and more insight over my resource usage and value. If a particular service proves to not be used often and perhaps I'll get better cost usage by cutting back on how often it is triggered, or how much we handle in a single batch... I can do what I need to. It is wonderful.

My Current "Domain" Stacks

So I've defined a pattern for the architecture. I have an SQS queue for each domain that subscribes to some sort of source data, most likely from an SNS topic from another domain. I have a single service that processes/adds that data from the queue into the database table for that domain. I have another service that can read from that table that is hooked up to an API Gateway endpoint. I actually have another service that can read in bulk from the table and provide to a different bulk endpoint. As data is stored in that table I have another service that publishes the change records to relevant SNS topics.

That's it... a typical domain stack contains around 1 SQS Queue, 4 Lambdas (Getter, BulkGetter, Adder, and Publisher), 2 API Gateway endpoints (Single and Bulk), 1 Dynamo Table, and 1 SNS Topic. Each domain's Queue is the entry of data to process and the SNS topic is the publishing of processed data, and the endpoints reads the processed data. It is cheap and awesome and easy to manage and add new features. Need a new materialized view? Duplicate the tech stack, point the queue to the topic of another stack, and voila new data in no time. You can see the current Alpha architecture below (kind of a mess right now, but you get the idea):

Still more to come

And this is just the infrastructure... there are things that I chose to do with the business logic that have also made things easier for adding new features. Another post for another time. Thoughts on my first LinkedIn post? Please drop me some feedback and thoughts. I'd love to hear them and improve for next time. I also live stream fairly regularly on LiveCoding.tv. Feel free to drop by, ask some questions and we can get into an awesome discussion there as well. :)