Chapter 8: Using Services

Chapter 8.

A service provides some capabilities that are needed by the rest of the application. Example include billing services (which offer the component that bills customers), account creation services (which manage the component that creates accounts), and notification services (which include functionality for notifying users of events and conditions).

A service is a standalone component. The word “standalone” is critical. Services meet the following criteria:

Maintains its own code base

A service has its own code base that is distinct from the rest of your code base.

Manages its own data

A service that requires maintaining state has its own data that is stored in its own datastore. The only access to this separated data is via the service’s defined API. No other service may directly touch another service’s data or state information.

Provides capabilities to others

A service has a well-defined set of capabilities and it provides these capabilities to other services in your application. In other words, it provides an API.

Consumes capabilities from others

A service uses a well-defined set of capabilities provided by others and uses them in a standard, supported manner. In other words, it uses other service’s APIs.

Single owner

A service is owned and maintained by a single development team within your organization. A single team may own and maintain more than one service, but a single service can have only a single team that owns and maintains it.

What Should Be a Service?

How do you decide when a piece of an application or system should be separated out into its own service?

This is a good question, and one that does not have a single correct answer. Some companies that “service-ize” split their application into many (hundreds or thousands) of very tiny microservices. Others split their application into only a handful of larger services. There is no right answer to this problem. However, the industry is trending toward smaller microservices, and more of them. Technologies such as Docker and Kubernetes have made these larger number of microservices a more viable system topology by providing an infrastructure to manage large number of small services.

We use the term services and microservices interchangeably in this book.

Divid ing into Services

So, how do you decide where service boundaries should be? Company organization, culture, and the type of application can play a major role in determining service boundaries.

Following are a set of guidelines that you can use to determine where service boundaries can be. These are guidelines, not rules, and they are likely to change and morph over time as our industry progresses. They are useful to help individuals begin thinking about services and think about what should be a service.

Here at a high level are the guidelines (in order of priority):

Specific business requirements

Are there any specific business requirements (such as accounting, security, or regulatory) that drive where a service boundary should be?

Distinct and separable team ownership

Is the team that owns the functionality distinct and separable (such as in another city, or another floor, or even just a different manager) that will help specify where a boundary should be?

Naturally separable data

Is the data it manages naturally separable from other data used in the system? Does putting data in a separate datastore overly burden the system?

Shared capabilities/data

Does it provide some shared capabilities used by lots of other services and does that shared capability require shared data?

Let’s now look at these each individually and figure out what they mean.

Guideline #1: Specific Business Requirements

In some cases, there will be specific business requirements that dictate where a service boundary should be. These might be regulatory, legal, security, or some critical business need.

Example 8-1 . Payment processing

Imagine your system accepts online credit card payments from your customers. How should you collect, process, and store these credit cards and the payments they represent?

In example 8-1, a good business strategy would be to put the credit card processing in a different service, separate from the rest of the system.

Putting critical business logic such as credit card processing into its own service is valuable for several reasons:

Legal/regulatory requirements

There are legal and regulatory requirements around how you store credit cards that require you to treat them in different ways from other business logic and other business data. Separating this into a distinct service makes it easier to treat this data differently from the rest of your business data.

Security

You might need additional firewalls for security reasons around these servers.

Validation

You might need to perform additional production testing to verify security of these capabilities in ways significantly stronger than other parts of your system.

Restricting access

You will typically want to restrict access to these servers so that only necessary personnel have access to highly sensitive payment information such as credit cards. You typically do not want or need to provide access to these systems to your entire engineering organization.

Guideline #2: Distinct and Separable Team Ownership

Applications are becoming more and more complicated, and typically larger groups of developers are working on them, often with more specialized responsibilities. Coordination between teams becomes substantially harder as the number of developers, the number of teams, and the number of development locations grow.

Services are a way to give ownership of smaller, distinct, separable modules to different teams.

[tip]A single service should be owned and operated by a single team that is typically no larger than three to eight developers. That team should be responsible for all aspects of that service.

By doing this, you loosen up the interteam dependencies and make it much easier for individual teams to operate and innovate independently from one another.

As previously stated, a single service should be owned and operated by a single team, but a single team can own and operate more than one service. The key is to make sure that all aspects of a single service are under the influence of a single team. This means that team is responsible for all development, testing, deployment, performance, and availability aspects of that service.

However, that one team can have the ability to successfully manage more than one service, depending on the complexity and activity involved in those services. Additionally, if several services are very similar in nature, it might be easier for a single team to manage all those services.

Separate team for security reasons

Sometimes, you want to restrict the number and scope of individuals who have access to the code and data stored within a given service. This is especially true for services that have regulatory or legal constraints (as discussed in Example 8-1).). Limiting access to a service with sensitive data can decrease your exposure to issues involved in the compromising of that data. In cases like this, you might physically limit access to the code, the data, and the systems hosting the service to only the key personnel required to support that service.

Additionally, splitting related sensitive data into two or more services, each owned by distinct teams, can reduce the chances of that data being compromised by making it less likely that multiple services with distinct owners will all have data compromised.

Example 8-2 . Splitting data for security reasons

In Example 8-1, the credit card numbers themselves can be stored in one service. The secondary information necessary to use those credit cards (such as billing address and CCV code) could be stored in a second service. By splitting this information across two services, each owned and operated by individual teams, you limit the chance that any one employee can inadvertently or intentionally expose enough data for a rogue agent to use one of your customer’s credit cards inappropriately.

You might even choose to not store the credit card numbers in your services at all and instead store them in a third-party credit card processing company’s services. This ensures that, even if one of your services is compromised, the credit cards themselves will not be.

Guideline #3: Natural ly Separable Data

One of the requirements for a service is that its managed state and data needs to be separate from other data. For a variety of reasons, it is problematic to have multiple independent code bases both operating on the same set of data. Separating the code and the ownership is only effective if you also separate the data.

Figure 8-1. shows a service (Service A) that is trying to access data stored in another service (Service B). It illustrates the correct way for Service A to access data stored in Service B, which is for Service A to make an API call to Service B, and then let Service B access the data in its database itself.

Figure 8-1.  

Figure 8-1 . Correct way to share data

If instead, Service A tries to access the data for Service B directly, without going through Service B’s API, as is shown in Figure 8-2., all sorts of problems can occur. This sort of data integration would require tighter coordination between Service A and Service B than is desired, and it can cause problems when data maintenance and schema migration activities need to occur. In general, the accessing of Service B’s data directly by Service A without involving Service B’s business logic in that process can cause serious data versioning and data corruption issues. It should be strictly avoided.

Figure 8-2.  

Figure 8-2 . Incorrect way to share data

As you can see, determining data division lines is an important characteristic in determining service division lines. Does it make sense for a given service to be the “owner” of its data and provide access to that data only via external service interfaces? If the answer is “yes,” this is a good candidate for a service boundary. If the answer is “no,” it is not a good service boundary.

A service that needs to operate on data owned by another service must do so via published interfaces (APIs) provided by the service that owns that data.

Guideline #4: Shared Capabilities/Data

Sometimes a service can be created simply because it is responsible for a set of capabilities and its data. These capabilities and data might need to be shared by a variety of other services.

A prime example of this principle is a user identity service, which simply provides information about specific users of the system. This is illustrated in Figure 8-3.

Figure 8-3.  

Figure 8-3. Using services to share common data with other services

There might be no complex business logic involved with this data service, but it is ultimately responsible for all the general information associated with individual users. This information often is used by a large number of other services.

Having a centralized service that provides and manages this single piece of information is highly useful.

Mixed Reasons

The preceding guidelines outline some basic criteria for determining where service boundaries should be. Often, though, it is a combination of reasons that can ultimately make the decision for you.

For example, having a single user identity service makes sense from a data ownership and shared capabilities perspective, but it might not make sense from a team ownership standpoint. Data for which it might make sense to store in a database associated with user identity might be better stored in a separate service or services.

As a specific example of such data, a user may have search preferences that are typically part of a user profile, but are not typically used by anything outside of the search infrastructure. As such, it might make sense to store this data in a search identity service that is distinct from a user identity service. This might be for data complexity reasons or even performance reasons.

Ultimately, you must use your judgment while also taking the preceding criteria into account. And, of course, you must also consider the business logic and requirements dictated by your company and your specific business needs.

Going Too Far

While splitting applications into services has many benefits as discussed above, often you can go too far. Creating service boundaries using the previously discussed criteria can be taken to the extreme, and too many services can be created.

For example, rather than providing a simple user identity service, you might decide to take that simple service and further divide it into several smaller services, such as the following:

User human readable name service

User physical address management service

User email address management service

User hometown management service

User ... management service

Doing this is most likely splitting things up too far.

There are several problems with splitting services into too fine a number of pieces, including overall application performance. But at the most fundamental, every time you split a piece of functionality into multiple services, you do the following:

Decrease the complexity of the individual services (usually).

Increase the complexity of your application as a whole.

The smaller service size typically makes individual services less complicated. However, the more services you have, the larger the number of independent services that need to be coordinated and the more complex your overall application architecture becomes.

Having a system with an excessively large number of services tends to create the following problems in your application:

Big picture

It becomes more difficult to keep the entire application architecture in mind, because the application is becoming more complicated.

More failure opportunities

More independent components need to work together, creating more opportunity for interservice failures to occur.

Harder to change services

Each individual service tends to have more consumers of that service. Having more service consumers increases the likelihood of changes to your service negatively affecting one of your consumers.

More dependencies

Each individual service tends to have more dependencies on other services. More dependencies means more places for problems to occur.

Many of these problems can be mitigated by defining solid interface boundaries between services, but this is not a complete solution.

The Right Balance

Ultimately, deciding on the proper number of services and the proper size of each service is a complicated problem to solve. It requires careful consideration of the balance between the advantages of creating more services and the disadvantages of creating a more complex system as a whole.

Building too few services will create problems similar to the monolith application, where too many developers will be working on a single service and the individual services themselves become overly complicated.

Building too many services will cause individual services to become trivially simple, but the overall application becomes overly complicated with complex interactions between the services. I’ve actually heard of an example application utilizing microservices that defined a “Yes” service and a “No” service that simply returned those boolean results—this is extreme taken to extreme. It would be great to define exactly what the right size is for a service, but it depends on your application and your company culture. The best advice is to keep this complexity trade-off in mind as you define your services and your architecture.

Finding the appropriate balance for your specific application, organization, and company culture is important in making the most use of a service-based environment.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.255.127