Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing SOA with RESTful service and application APIs?

At the moment we have one huge API which is used by our backoffice, our frontend, and also our public API.

This causes me a lot of headaches because when building new endpoints I find a lot of application specific logic in the code which I don't necessarily want to include in my endpoint. For example, the code to create a user might contain code to send a welcome email, but because that's not needed for the backoffice endpoint I will then need to add a new endpoint without that logic.

I was thinking about a large refactor to break our code base in to a number of smaller highly specific service APIs, then building a set of small application APIs on top of those.

So for example, an application endpoint to create a new user might do something like this after the refactor:

customerService.createCustomer();
paymentService.chargeCard();
emailService.sendWelcomeEmail();

The application and service APIs will be entirely separate code bases (perhaps a separate code base per service), they may also be built using different languages. They will only interact through REST API calls. They will be on the same local network, so latency shouldn't be a huge issue.

Is this a bad idea? I've never seen/worked on a codebase which has separated the two before, so perhaps there is a better architecture to achieve the flexibility and maintainability I'm looking for?

Advise, links, or comments would all be appreciated.

like image 810
user7862512 Avatar asked Jan 18 '18 17:01

user7862512


2 Answers

Your idea of making multiple, well-defined services is sound and really it is the best way to approach this. Going with purely micro-services approach however trendy it might seem, proves to be an overkill most often than not. This is why I'd just redesign the existing API/services properly and follow solid and sound SOA design principles below. Good Resources could be found on both serviceorientation.com and soapatterns.org I've always used them as reference in my career.

Consider what types of services you need

Service types and Layers (image from serviceorientation.com)

  • Entity services are generally your Client, Payment services - e.g. services centered around an entity in your domain. They should be business-agnostic, and be able to be reused in all scenarios. They could be called sometimes by clients directly if sufficient for their needs. They could be called by Task services.
  • Utility services contain logic you're likely to reuse in other services, but are generally not called by the clients directly. Rather, they'd be called by Task and Entity services. An example might be a Transliteration service.
  • Task services combine and reuse Entity and Utility services into meaningful tasks. Most often they are not that agnostic and they do implement some specific business logic. They have meaningful business operations and they are what clients mostly call.

Principles to follow when redesigning

I strongly recommend going over this cheat sheet and making sure everything there is covered when you do your redesign. It's great help.

In general, you should make sure that:

  1. Each service has a common context and follows the separation of concerns principle. E.g. Clients service is only for clients related operations, etc.

  2. Each of the Entity and Utility services is business-agnostic and basic enough. So it can be reused in multiple scenarios and context without being changed. Contract must be simple - CRUD and only common operations that make sense in most usage scenarios.

  3. Services follow a common data model - make sure all the data structures you use are used uniformly in all services in order to prevent need for integration efforts in the future and promote combination of services for clients to exploit. If you need to receive a customer that another service returns, this should be happening without the need for transformation

OK, but where to put the non-agnostic logic?

Now, you have multiple options for abstracting business logic whenever you have a need for complex business functionality. It depends on your scenario what you're going to chose:

  • Leave logic to all clients. Let them combine your simplified services
  • If there is business logic that is commonly implemented in multiple of your applications and has the potential to be reused heavily you can implement a composite service that reuses multiple existing underlying services and exposing the logic.

Service Composability. Concerns on multiple API calls communication overhead.

Well, this is an age-old question - should you make multiple API calls when they will probably create some communication overhead? The answer is - it depends on how complex your scenario is, how much reuse you expect and how flexible you want to be. Also is speed critical? To what extent? In Service Oriented Architecture though, this is a very common approach - to reuse your existing services and combine them in new configurations as needed. Yes, it does add some overhead, but I've seen implementations in very complex environments, for example Telecoms, where thanks to the use of ESB solutions, message queues, etc the overhead is negligible compared to the benefits. Here is a common architecture approach (image from serviceorientation.com):

Composing Web Services to Solve a Bigger Problem

The mandatory legacy refactoring heads-up

More often than not, changing the established contract for multiple existing client systems is a messy business and could very well lead to lots of refactoring and need for looking for needle-in-a-stack functionality that's somewhere deep in the (possibly) legacy code. Business logic might be dispersed everywhere. So make sure you're ready and have the controls, time and will to lead this battle.

Hope this helps

like image 190
Plamen Petrov Avatar answered Oct 05 '22 12:10

Plamen Petrov


Is this a bad idea?

No, but this is a big overall question to be able to provide very specific advice.

I'd like to separate this into 3 areas:

  • Approach
  • Design
  • Technology

Working backwards, the Technology is the final and most-specific part, and totally depends on what your current environment is (platforms, skills), and (hopefully) will be reasonable self-evident to you once the other things are in progress.

The Design that you outlined above seems like a good end-state - having multiple, specific, focused APIs, each with their own responsibility. Again, the details of the design will depend on the skills of you and your organization, and the existing platforms that you have. E.g. if you are already using TIBCO (for example) and have a lot invested (licenses, platforms, tools, people) then leveraging some of their published patterns/designs/templates makes sense; but (probably) not if you don't already have TIBCO exposure.

In the abstract, the REST API services seems like a good starting point - there are a lot of tools and platforms at all levels of the system for security, deployment, monitoring, scalability, etc. If you are NGINX users, they have a lot of (platform-independent) thoughts on how to do this also NGINX blog, including some smart thinking on scalability and performance. If you are more adventurous, and have an smart, eager team, a look at Event-driven architecture - see this

Approach (or Process) is the key thing here. Ultimately, this is a refactoring, though your description of "a large refactor" does scare me a little - put that way, it sounds like you are talking about a big-bang change and calling it refactoring. Perhaps it is just language, but what's in my mind would be "an evolution of the 'one huge API' into multiple, specific, focused APIs (by refactoring the architecture)". One place to start is Martin Fowler, while this book is about refactoring software, the principles and approach are the same, just at a higher-level. Indeed, he talks about just this here

IBM talk about refactoring to microservices and make it sound easy to do in one step, but it never is (outside the lab).

You have an existing API, serving multiple internal and external clients. I will suggest that you'll want to keep this interface solid for these clients - separate your refactoring of the implementation from the additional concerns of liaising with and coordinating external systems/groups. My high-level starting approach would be:

  • identify a small (3-7) number of related methods on the API
    • ideally if a significant, limited-scope change is needed anyway with these methods, that is good - business value with the code change
  • design/specify a new stand-alone API specifically for these methods
    • at first, clone the existing model/naming/style
  • code a new service just for these
    • with proper automated CI/CD testing and deployment practices
    • with associated monitoring
  • modify the existing API to have calls to these methods re-direct to call the new service
    • perhaps have a run-time switch to change between the old implementation and the new implementation
  • remove the old implementation from codebase
  • capture issues, assumptions and problems along the way
    • the first pass will involve a lot of learning about what works and doesn't.
  • then repeat the process over & over, incorporating improvements each time.

At some point in the future, when appropriate due to other business-driven needs, the API published to the back-end, front-end and/or public clients can change, but that is a whole different project.

As you can see, if the API is huge (1,000 methods => 140 releases) this is a many-months process, and having a reasonably frequent release schedule is important. And there may be no value improving code that works reliably and never changes, so a (potentially) large portion of the existing API may remain, just wrapped by a new API.

Other considerations:

  • public API? Maybe a new version (significant changes) will be needed sooner than the internal APIs
    • focus on the methods/services used by it
  • what parts/services change the most (have the most enhancement requests approved)
    • these are the bits most likely to change, and could benefit most from a better process/architecture
  • what are future plans for change and where would the API be impacted
    • e.g. change to user management, change to payment processors, change to fulfilment systems
    • e.g. new business plans (new products/services)
    • consider affected methods in the API
  • Also see:
    • Using Microservices for Legacy System Modernization
    • Migrating From a Monolith to APIs and Microservices
    • Break the Monolith! Loosely Coupled Architecture Brings DevOps Success
    • From the CEO’s Desk: Application Modernization – Assess, Strategize, Modernize! 9
    • [Microservices Architecture As A Large-Scale Refactoring Tool 10

Probably the biggest 4 pieces of advice that I can give is:

  • think refactoring: small changes that don't affect function
  • think agile: small increments that are valuable, testable, achievable
  • think continuous: have a vision for where you will (eventually) get to, then work the process continuously
    • script & automate the processes from code, documentation, testing, deployment, monitoring...
    • improving it every time!
  • you have an application/API that works - keep it working!
    • That is always the first priority (you just need to work to carve-out time/budget for maintenance)
like image 29
Brian Quinn Avatar answered Oct 05 '22 10:10

Brian Quinn