Skip to content

Instantly share code, notes, and snippets.

@digitalwm
Last active December 12, 2015 01:38
Show Gist options
  • Save digitalwm/4692290 to your computer and use it in GitHub Desktop.
Save digitalwm/4692290 to your computer and use it in GitHub Desktop.
SaaS made simple
I had many attempts to start writing stuff, and I guess will still have in the future, but for now I decided to try to write something about SaaS and how things can be done simple. I will try to provide use cases and examples of what can be good and what can be bad based on my previous experiences and my current ones.
I tried to read and look for this type of documentation for a long period of time and I could not find one, that was pure and simple. Most of them were focused on fancy terms, or specific examples to some programming languages or specific platforms. So I decide to provide a different approach, not very academic but now also "for dummy" approach.
What do I expect from any reader to know?
Well this is a very interesting question. I would expect for people to know what internet is, what is a web server, what is XML or JSON, what is a socket and mostly basic IT knowledge. But even so, when you don't understand something, would recommend to try to look it up on Google or Wikipedia.
What would you get in the end?
I would really hope that by the end of these series of "texts" :), to understand what SaaS is and how could help your company or projects. I don't intend to cover all possible options and scenario, and also please remember that this is not an SaaS encyclopedia.
What is this SaaS thing?
The concept is pretty nice and interesting, but before you can get a general idea of what it is, we should at least try to define what it stands for. So SaaS it stands for Software as a Service. Sounds pretty geeky and cool, or not :). The overall idea of this is to have you software not made as a one big - huge block, that runs standalone and consists of modules linked together and depended on each other, but to have it as a group of services that are stand alone, can be build using different programming languages and can exists in separate environments from each other.
What is a service?
A service is very similar to a web page, that based on different input provided on request it will provide you a structured reply / response back. This reply is not HTML with colors and shiny pictures is a data structure that can be XML, JSON or even your own design.
There are many types and approaches on the concept of services. The services can be binded to sockets and use proprietary protocols, can user unix pipes as methods of communication, can be web servers that handles REST or SOAP, but all of them should have the same concept. Accept a request process and deliver the result.
I will approach for now the web services, because are the most common and pretty easy to implement.
Now lets try to take an example. We will build (at least from the design and concept) a small billing platform for games. Just for people that are very curious, this is not what I am working as a job. So no company secrets spilled in the internet. ;)
What to do?
First step in any project would be to gather and document the features that our platform will need to implement. A very good approach would be to do a brain storm and create a list of things that needs to be implemented and after try to break them down in more detailed things.
What should it do?
Well, what a billing product should do? It should be able to have products to sell, should be able to store transactions of the purchase, should be able to have wallets and user inventory, should have a way to inform game about items it has.
We have now a rough idea what it should do. Lets try now to break it into parts / services. What functionalities can be grouped and isolated as standalone.
The products service - here will try to store, manage and provide operations to everything related to products
The user service - here will try to store, manage and provide operations to everything related to our users / customers
The transaction service - as specified above, everything related to the transactions.
The wallet service - this one will manage all the user inventory and wallets and their content
The game middleware - this service will be the entry point of the game into our system, by this communication channel the game should be able to query and consume some parts of our API.
The payment middleware - this service will be the communication point with the external provider of the payment.
All of those should be enough for now, and the good part of this SaaS approach is that, will be pretty easy to add new services and by that new features.
Conclusion...
Now we have a rough idea what we want to build, how the services are separated. The next step would be to decide how the services will comunicate, in what format (message structure) and in what architecture (layout). But that, next time.
Welcome back. So we decided last time what we want to build and what need to be built. Now will try to assemble the architecture, the communication methods and the message structure.
Lets start with the architecture. We have now separated and independent systems. Each and one of them can and should be able to exist without the other. So how will all of them connect?
There are multiple structures that can apply like:
Star - One central service that connects with all the other service, and communication between external services is done via the central service. This style is pretty useful, when we need a central and unique entry point, or an unique communication channel. Could be applied to systems that provide (centralized events, chat, message queues, offering external access to a cloud of internal services).
Spider - A cluster of central services and each node of the central service is connecting to specific services. It is like a body of a spider and each part of its body is connected with a pair of legs. Useful when you have the central system distributed over multiple nodes or connecting multiple data centers. (As example we could have a company with multiple branches, each and one of them providing a specialized service / API. So a central communication trunk is created, and each one of the central nodes announces what services / API is providing)
Mesh - Each service is able to communicate with each and other service available. This provides a good functionality lifetime, because if one service is failing the others are still running and ready. Also this can be applied when the functionality of the services are independent and the process / action flow is not well defined or must be able to provide custom ever-changing flows.
Mixed - Any of the above combined, based on necessity of the group of services desired.
Our existing project could be approached with any of the above architectures. I personally would recommend the mesh approach.
Why would you use that one?
Well I personally think that you can have more advantages and more flexibility. For example you want to replace an existing service with a new one (complete rewrite) or with an external one. The changes that you must bring to your existing structure is almost zero. Lets say that you will want also to add a new set of features or maybe a new service even. Because everything is independent the changes that you made will not affect the existing systems. When needing a new process flow, you just need to include the new services developed in your use cases, and again no development required on the existing structure.
And why the other are not so efficient?
The question is not so correct. Each structure can handle different scenario. But lets try to see what issues we might have if we would approach other system structure that the one mentioned.
Using the star architecture, every time we would add a new functionality on an existing service, or add a new service, we would need also to change the handling in the core service. Here can be implemented some basic service discovery so the development for the core service would be minimal when a new service would be added. A downfall for this architecture is the "one point of failure", if the core fails than everything fails.
Using the spider architecture, would involve the same issues as the star architecture, but would fix the "one point of failure" problem, or at least partially. When a core node will fail, all the services attached to it will also fail, but the rest of the platform will still be running.
In the end you are free to choose any that fit you or even invent yourself one. A good inspiration point nature. You can find each architecture type in the real life, and even measure it's efficiency. For example the mesh structure can be found in the neuronal structure of the human brain. The very important idea is to protect your system from fail overs, have a 100% message success rate or as much as possible closed to this number and have your services ready to be cloned (have more instances) for load balancing.
How will they communicate?
We have decided one our structure, but how will they communicate. What protocol? What structure? Sync or Async?
Lets split it again in small problems. Will start with the used protocol. Because we are developing now web service, I would recommend to use HTTP calls against normal web servers.
As message structure there are again lots of options:
XML
SOAP XML
HTTP parameters
JSON
Custom
I will not describe each of the above, you can easily find details on google or wikipedia. Most of them are either with allot of overhead (is useless data that create additional network traffic, or take memory space) or hard to manage or even not secure.
For example XML or SOAP XML have allot of overhead information, and so will take more time to transmit data and more memory consumed while storing it in your service.
HTTP parameter have no overhead but are hard to manage and read from requests, while JSON is not always secure, being easy to manipulate by man in the middle technique (where is applicable).
For this demonstration will use a Custom made message structure that combines some of the above.
So, how will the message and communication look?
Will use HTTP requests and send as a POST parameter a JSON encoded message. Will protect our messages with a generated hash based on a shared secret and the message structure. Will try to implement also a unique message identifier, so our messages will be ready for queuing when will implement one.
Let's try to assemble the message:
{
"messageID" : "<generated message id>",
"requester" : "<requester identifier>",
"provider" : "<provider identifier>",
"action" : "<function to be called on service>",
"parameters" : {
"param1" : "val1",
"param2" : "val2"
},
"hash" : "<generated hash value>"
}
I hope that doesn't seem to complicated, or at least you have the basic knowledge to grasp that. Lets dissect the message:
messageID - is an unique message identifier generated by the requester in order to track a message across your services. It is used in the reply and very useful when pulling messages from a queue, processing them and giving a reply to the request.
requester - is an identifier that would point to the service or the consumer that is making the API request. Can be string, integer, uuid or anything that would help you mapping it.
provider - is an identifier that would point to the service that you are querying. It is useful to have if you are having services that proxies some messages or using queues.
action - should be the name of the function that is processing the request. It's not mandatory to reflect the actual code function if you are using any kind of routing.
parameters - is a JSON encoded object, that contain the parameters to your request as key value structure.
hash - is a short / medium hash could be md5 or sha1 encryption of the message structure concatenated with the shared secret. Please note that you should not include in the hash the "hash" variable of the message.
Why all this complications?
Its not necessary complicated but could provide some advantages.
Easy to apply filters on it, compared to normal HTTP variables.
Can provide message size limitation and offer buffer overflow protection.
Can be pushed to a queue system without any changes.
Protects against man in the middle by using the hash.
Next time will discuss what the reply message should look like, and what precise features we must implement in the designed services.
Reply message
Last time we have seen how the request message should look like. Now it is time to close the circle and decide how the reply message should be.
{
"messageID" : "<received messageID from request>",
"requester" : "<requester identifier>",
"provider" : "<provider identifier>",
"code" : "<error code>",
"message" : "<error message>",
"return" : {
"var1" : "val1",
"var2" : "val2"
},
"hash" : "<generated hash value>"
}
Let us now break the message into smaller pieces:
messageID - Will be filled in with the message ID that we received in the request. This way we could match a request with a reply
requester - is an identifier that would point to the service or the consumer that is making the API request and now will receive the reply. Can be string, integer, uuid or anything that would help you mapping it.
provider - is an identifier that would point to the service that you are querying and now will emit this reply. It is useful to have if you are having services that proxies some messages or using queues.
code - is an error code, numeric, that will useful to trigger custom steps in case of errors. A good idea would be to use value 0 for no error and anything greater as specific error code
message - is a string value that will contain a detailed explanation if an error occurred. A good idea would be to be empty in case of success.
return - is a JSON object that will contain all the returned data in the reply message. This way each API call can return a quite complex data structure.
hash - is a short / medium hash could be md5 or sha1 encryption of the message structure concatenated with the shared secret. Please note that you should not include in the hash the "hash" variable of the message.
Explaining the Hash
What is the scope and idea of the hash, one might ask. Well this is quite simple. It is used as CRC, very similar as any archiver uses it to check integrity of the archive. The hash is generated based on the initial message and using a shared secret / password. If at any point in time while the message is transported from source to destination the information is changed (by good or bad intention), the CRC check on destination will fail and the request will be rejected.
You might consider this in some cases an overkill because using HTTPS or any SSL encrypted connection would suffice. Both of the options are very secure, but both can be hijacked if the transport is done via third parties network. Considering that the source and destination are secured (so the shared secrets remain secret) no data injection will be possible, even when the route of transport is compromised.
Breaking the services into API's
In the first part we decided what we want to build to be able to accomplished the goal that we decided for this project. Now each service will need to be broken into smaller parts so we would know very exact what functionality / features we will have.
Product service
As an overall feature, this service should be able to store all the relevant information regarding our digital products like (name, prices, availability informations, logos, etc.)
What actions we will need for this service?
Create - creates a new product
Get - will read all the product information for a specified product
Update - will update the product information
List - will list all the available products
Query - will get a smaller list of products based on the input provided (countries, price range, etc., category)
Activate / Suspend - will activate or suspend based on a period of time the respective product
User service
As an overall feature, this service should be able to store all the relevant information regarding our users like (name, age, address, etc.)
What actions we will need for this service?
Create - will create a new user account
Login - will validate an user account
Get - will read all the user information for a specified account
List - will list all the users in the system using pagination
Ban / Unban - will ban or unban an user account
Activate / Suspend - will activate or suspend an user account
Send Message - will send the user and email with the content specified as parameters
Transaction service
As an overall feature, this service should be able to store all the relevant information regarding user purchase transactions. Every time a user will purchase (with real money / hard currency) one or more items in one action, one entry will be created in this services
What actions we will need for this service?
Create - Will create a new transaction
Get - Will read all the details of a transaction
Update State - will update the state of a transaction (started, in progress, payed, canceled, charged-back)
Wallet service
As an overall feature, this service should be able to store all the relevant information regarding the virtual items / currencies of an user. The scope of this service will be to keep a virtual wallet for each user in the system.
What actions we will need for this service?
Create - Will create a new wallet for a user. Will be allowed only one wallet for a user.
Add - Will add one or more items to the wallet
Consume - Will remove one or more items from the wallet
Get - Will read all the items in a specified wallet
Game middleware
This service should provide a bridge between any game or games that we have and our billing platform. So this service will provide external standardized REST call for:
Get - Reading the balance of a wallet
Consume - Consuming on or more items from the wallet
This part of our platform will be one of the outside doors. Itself will make internal calls to the User service and Wallet service.
Payment middleware
This service will be the outside door to the payment platform. This can be anything (from our own develop one to Paypal or any other gateway). This service should provide some internal available actions:
Create - Will create a new payment transaction
Get - Will get the actual status of a payment transaction
And also will provide some external calls that will handle callbacks from the payment gateway (when the payment will be completed)
Conclusion
So we have finalized our message structure and also defined what API should each service provide. In the next part we will try to get the basic flow of data within our platform.
Process flow
So we have our services defined, but how will all the processes happen? In a normal application the process is defined by the order in which certain modules and functions are called. In a service based architecture we can extrapolate this process into calls made to specific services and their API's. So let us approach this issue by examples.
Setup
Let us imagine that our company is a game developing company and we have 4 games on the market. Each game will use our wallet service as a the in-game wallet and will have it's own client and it's own community portal. Based on this fictional setup we might have the following processes.
Registration
When a new user will land on one of our portals and he will be amazed by our games he will want to register. During this process the portal will need to make the following API's calls:
User Service - Create (user)
Wallet Service - Create (wallet)
By this process we have for the new user everything ready and running in case he will want to purchase anything or enter the game and start playing.
Login
When an existing user will enter his credentials on any of the portals that we have, the portal will have to do the following API calls:
User Service - Login (user)
Wallet Service - Get (user wallet)
At the end of the process the portal will be able to display the user details and user's active wallets and their content
Enter Shop - Portal
When a user will want to buy different products for hard currency and will go on with the purchase to completion, we will need the following call stack:
Product Service - Query (available products)
Transaction Service - Create (new transaction)
Payment Middleware - Create (new payment transaction)
At the end of the flow we will have a payment transaction started where the user will need enter valid payment credential.
Payment was completed (success or fail)
When the payment system / platform (which we consider it to be external) will complete a transaction, it will inform the payment middleware about the transaction status. The notification should happen even in case of success or failure. On its turn the middleware will have to do the following call stack:
Transaction Service - Update state (with the correct state)
Transaction Service - Get details (what products were bought)
Wallet Service - Add (items to the wallet)
User Service - Send message (send a transaction summary email)
Items consumed in the game
When one or more items are consumed in any of our games like (virtual items or virtual currency), the game will call the game middleware part which on it's side will do the following call stack:
Wallet Service - Get (the amount of items in the wallet)
Wallet Service - Consume (only if the required amount of items are available in the wallet)
The service on its turn will return the status of the end operation
Other flows
It might be that I did not cover all the possible flows (for sure I haven't - think about a backend administration tool), so when you need to figure out a new call stack try thinking of it in Lego style. You have many small pieces that you know what they are doing and you need to build something larger. Using the service architecture you can create at any time any flow you need without changing anything within the services that builds up your platform.
Development
The scope of this documentation is not how to teach you programming or what developing language / platform is the best. This why I leave it to your decision. All the services that builds up our software should be possible to be developed in any language available on the market (from Java, C#, Ruby, PHP to NodeJS and other).
What you should keep your focus while developing it are the following facts:
Keep you API response time as small as possible (less than 50ms should be good, less than 100ms should be decent)
Do validity check on the message before starting any framework behind your API's. This way wrong or malformed messages will not load up your service (will provide you some additional protection against inside DDOS attack)
Every time return something. By this I mean that even if you database is away or some exception crashed your code, you should be able to return to the caller an well formed reply with an error in it. This way you will keep you services rigid and stable.
Conclusion
So we have seen how data will flow between our services and what to keep in mind while developing it. In the next part will try to approach a basic server setup for our services, how scalable are we and how can we scale when needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment