Code reuse in micro services

It seems to me that one of the big questions around micro services at the moment is how to share code between the services that make up the overall system. This is important because you should be able to deploy individual services independently of others. So what can you share and how should you do it?

I don’t have a straight answer, but I do have a suggestion for how to think about the problem. Imagine that each service is implemented by teams in different companies, each with their own private source repositories. How would you share code then? For me, the answer is through shared dependencies (JARs of compiled classes in the case of Java). And those shared dependencies should probably only contain utility code such as that provided by commons-lang and Guava (Java examples again).

I guess shared client API JARs could work too if you don’t want to code against JSON/XML directly. The key code that _shouldn’t_ be shared is that related to the internal model of the service. The public API though is not internal.

So, is this a reasonable or even helpful way to look at this issue?

10 thoughts on “Code reuse in micro services

  1. Pingback: Sharing code between Microservices? | Simplicity Itself | Expert advice on handling change, for everyone

  2. eduardo

    Hi Peter,
    I guess that for Java that would be one solution, what if you have mixed microservice technologies like java and NodeJS all together ?

    Maybe using queues so you can decouple stuff, it depends on the kind service

    I hate coupling jar between several apps, then if you change the implementation of that jar you need to deploy all apps that relies on that library even if you don’t change the public interfaces

    Regards,
    Eduardo

  3. Peter Post author

    Eduardo,

    As far as mixed platforms go, it’s hard to share code between them. There is a natural barrier there that fits the (micro) service model well and forces you to keep the code bases independent.

    When it comes to sharing JARs, I think you should still treat each service as if it were developed by independent teams. In such cases, you develop your service against a specific version of a JAR (rather than “latest”). If someone releases a new version of that JAR, that’s fine because you can ignore it. You should only upgrade a service to a new version of that dependency when you want to.

  4. eduardo

    Hi Peter,

    yes that is how we work right now, but that is still a pain when that jar is shared across several applications and just for one change, for example changing the way the VAT is calculated )in some sum or whatever done by that library) then it forces you to change that library version in all your apps and redeploy all of them.

    Another issue that I had is trying to find a bug that was happening in one app but not in the other ones and then we saw that the app was using an older version where the bug still existed

    🙁

  5. Pingback: Diario di Grails (settimana 2 del 2015) | BME

  6. Peter Post author

    Those are a couple of interesting examples, particularly the VAT calculation. I would argue that in most cases, VAT calculations should be treated as part of the business logic (part of the internal model) and hence shouldn’t be shared between services. More importantly, if more than one service performs VAT calculations, then any change to the algorithm will necessitate the redeployment of all those services, regardless of whether they share the calculation code via a library or duplicate it in their own code bases.

    In this case, you have to consider whether VAT calculations should be hived off into a single service that the rest of the services then use. A change to the VAT calculation then means that only one service needs to be redeployed, which is what you’re after. That said, there may be performance costs associated with such an approach, which would require some mitigation.

    As for the bug you encountered, that is symptomatic of taking a multi-service approach I feel. At least by using a shared library you can readily determine which services are impacted. It’s then up to you to put the requisite tests in place to check whether the bug affects a given service or not. If yes, then it needs a redeployment with the newer library.

    I guess one of the biggest challenges in service-oriented architectures is determining the service boundaries, i.e. exactly what each service should be responsible for. It’s just another aspect of how you model the system. And getting the model right has never been an easy process 🙂

  7. Owen Rubel

    I solved this in the Grails API Toolkit, gave a talk on this at SpringOne and just did another talk on this last week at the Silicon Valley Grails Meetup (http://www.slideshare.net/bobdobbes/silicon-valley-grails). The issue lies in the fact that IO state is bound to business logic (by design) in the application and thus cannot be shared.

    The fix is to separate communication logic from business logic and thus separate IO state bindings as well from business logic. What I mean by this is that we bind IO state through JAX, Spring Security and other annotations and service in controllers can’t thus we can’t share that state.

    We need to separate that state by first separating the logic into a communication layer with push/pull capabilities (like HandlerInterceptor) so it can talk to itself (unlike controller) and then we can move IO state into common shareable object. At that point, this object can be cached, synced, updated, shared across all architectural components (ie proxy, api instances, api gate, MQ, etc)

    See my slideshare for a good idea of what I’m talking about and download the new Grails API Toolkit when I am able to push the nex version; am blocked today as they are updating something and process is broken.

  8. Peter Post author

    I had a look at the presentation, but I’m still struggling to understand what is meant by I/O state and what’s bound or shared. Are there any concrete examples? At the moment, it’s not clear to me what the API object does or what its value is.

  9. Owen Rubel

    The video explains it a bit better (https://www.youtube.com/watch?v=mZOs7oz0JOI). I/O state is th separation of I/O data from communication/business logic so it can be shared. When we build out the architecture, we will move communication services to request tooling and response tooling (ie proxy,MQ). For example, we may want the proxy to check ROLE and input for an api call ahead of time and output data for the api call at the MQ. I/O state is data that can be shared in a common object which can be shared across architecture for IO flow. So we can sync that data and version it separate from functionality.

    For example, if we should have an ADMIN_ROLE and a USER_ROLE and the ADMIN_ROLE gets a larger dataset as defined by the IO state, we can at any time add data to the USER_ROLE or subtract that data from the ADMIN_ROLE (as long as that data exists within the definition) and then reversion. This allows us to send and cache one common object as a resource and simplify.

    And by sharing IO state, we are better able to keep things CPU bound vs IO bound.

  10. Owen Rubel

    I guess the easiest way to explain IO state is to say that when IO data needs to be shared across the architecture and as such, needs to be separate from functionality. If the IO Data (roles, request method, required input data, required output data, api uri, etc) is bound to the controller/method, this is ‘bound IO state’.

    If this data is separated into a shared object which can be synced across architectural components, this is ‘shared IO state’

Leave a Reply

Your email address will not be published. Required fields are marked *