Services Infrastructure – Part 1

Here at Fundbox, one of the first design decisions we made was to develop a service-based system. Our system is divided into several independent components (the services), each having a distinct logical role within the system. There are several advantages to this approach – it’s easier to test each component independently, the overall system can scale more flexibly (as each component can be scaled separately), and code can be reused, which is always great.

When we started designing the infrastructure for our service-based system, we had the following goals in mind:

  • Business logic should be independent of the communication layer – it should work with different kind of server frameworks

  • Implementing the business logic should be as simple as possible – when developing a new service, the effort should go to creating the business logic, not lengthy integrations

The last goal was particularly important to us, as we didn’t know how well the server framework we chose initially will scale. So in case we needed to migrate to another framework in the future, code changes would be kept minimal. Several design iterations later, we got to the following implementation which works great for us. It’s written in Python, our core backend-development language, and we absolutely love it.

An example service:

Simple enough for you? It’s almost the most basic Python class you can write. Now, let’s look at the little tweaks:

  • ExampleService inherits from ServiceBase – that’s where the magic happens.

  • ExampleService has a class attribute named CURRENT_API_VERSION – allowing management of incremental API versions

  • ExampleService’s methods are decorated with @expose_method – which exposes them for public use by other services, as opposed to undecorated methods like _private_stuff, which can’t be accessed externally

This design makes it very easy is to develop a new service or modify an existing one: the implementation is independent of the server and communication framework, and the class only contains the “pure” business logic. Another benefit this has is that the logic can be tested independently of whichever server or communication framework is chosen. Simply create an ExampleService object and test it directly.

Let’s dig in a little deeper and see how this is done:

The ServiceBase class & expose_method decorator:

The decorator is fairly simple. It utilizes the fact that everything in python is an object, including methods. The decorator adds an attribute called exposed to the method it wraps:

The ServiceBase class is used to decouple the service logic from the underlying framework – the service code is independent of the framework used, and the framework doesn’t change based on which service it’s executing. Additional functionality can be added to all the services (like any parent class) – a service heartbeat or error query, for example. The ServiceBase code is fairly simple:

As you can see in run_method, we utilize the exposed attribute to discover if the method is public (After checking if the method exists). We return a tuple of success & result, which in the case of an error includes the error class and error message.

In the next post we will go over the communication layer, the client side and error handling.

Fundbox helps small businesses to fix their cash flow by turning unpaid invoices into money in the bank. Apply now to start clearing your unpaid invoices.

  • Shlomi Matichin

    this is the exact concept from cherrypy… why not use it instead of writing one?

    • Ophir Horowitz

      First off, great question! CherryPy is a great package, but as I wrote, one of our main goals at the time was not having to commit to using a specific web framework or communication layer. Although the end result may be similar, we couldn’t really easily use the CherryPy method exposing technique while keeping the option to have a different communication layer (for example, SOAP, which may be the protocol of choice for some of our business partners).

      Another advantage of this infrastructure is that there’s no parameter processing: In contrast, CherryPy mandates that you use string params only (which are translated to HTTP GET params), so when passing integers, lists or any other types, both the client side and server side need to include a serialization and de-serialization code. With our infrastructure, the communication layer is totally decoupled, and so floats, lists, dictionaries etc. can be passed with no pre or post processing. The infrastructure handles serialization of parameters, making the client and service code really nice and simple, focusing exclusively on the business logic. This will be made clearer in my next post.

      Also, as with probably any infrastructure, what’s presented here is the result of an evolution, and is not the full extent of our implementation. Our initial communication layer was implemented using Flask (SPOILER ALERT: we still use Flask for that), with decoupling happening afterwards.

      So, to conclude, although the final result would have been similar had we used CherryPy, keeping the flexibility and decoupling of business logic communication was more important to us.