DEV Community

Cover image for Microservices: How to use Null Object Pattern to improve user experience
Frol K.
Frol K.

Posted on

Microservices: How to use Null Object Pattern to improve user experience

Topics:

  • What is a Null Object Pattern?
  • How to apply
  • How to implement
  • Testing noncritical dependencies
  • Conclusion

What is a Null Object Pattern?

Resilience of microservice architecture comes from its ability to quickly recover from failures and being able to service customers in the process of recovery. And all this should happen without significant degradation of customer experience. The Null Object Pattern helps to target the latter - ensure that customer experience doesn’t degrade much when a non-essential part of the system is not working. This pattern could be applied in many levels of the tech stack: starting from when a service for a frontend component is not available (see pic example with status bar) and finishing up when you can't get data for an object or a class within a (micro) service in the backend.

Pic 1. Example when failure of status-bar block causes service denial for whole web page
Pic 1. Example when failure of status-bar block causes service denial for whole web page

How to apply

  1. you need to come up with a list of web pages, app screens, API endpoints, etc which are crucial from a business perspective.
  2. fill a list of all external dependencies for each item that can cause a denial of service.
  3. decide if this dependency is actually critical for the user's intent (e.g. if failure of loading recommended items should block the checkout process).
  4. noncritical services that cause more failures first.

List of critical endpoins

How to implement

Implementing this pattern is straightforward. If your method (such as a factory or repository) can not retrieve data for the object, simply return a null object. For the implementation in your language check out wikipedia and below you can see the diagram to get the idea:

Pic 2. Interface diagram of implementing Null Object pattern
Pic 2. Interface diagram of implementing Null Object pattern

However, blindly applying this pattern everywhere is a bad idea especially if the object's data is used later in modification queries... Imagine you have a repository that retrieves data for a user from the other service. If retrieval of the user fails, then your repository returns a null object with defaulted values (ID is set to 0, Name is set to “empty name” and so on). You can’t use it for writing queries otherwise your business logic will be inconsistent. So how to deal with this?

One of the options you have is to split your interfaces of the user object into two types: ones that could be implemented by Null Object and others that can’t. This separation is crucial to make sure that Null Object will never be used in writing queries.

Testing noncritical dependencies

It is relatively easy to implement for services with clear bounded contexts (e.g. recommendation block), however for commodity services like a user profile it isn’t that simple and requires a lot of hard work to turn it into a noncritical dependency. This happens because every request to the backend usually ends up with querying these services to render a name or other (in most cases usually) trivial information. To make sure that all this effort is not wasted it’s wise to enforce such behavior by introducing noncritical dependency testing.

One way to implement this type of testing is to make them a special case of your e2e regression test suite. The only difference is we re-route traffic for the dependency we want to artificially fail to a service that mocks failing behavior like responding with error or times out after 5 sec for example. See the example in the picture below:

Pic 3. Example of testing mocking user service with bad <br>
behavior. Mock tests make a request with a header to turn off user-service. This header got propagated down the stack and when reaches user-service it’s got rerouted to mock with mocked behavior
Pic 3. Example of testing mocking user service with bad
behavior. Mock tests make a request with a header to turn off user-service. This header got propagated down the stack and when reaches user-service it’s got rerouted to mock with mocked behavior

For this implementation, we need a sidecar that reroutes traffic based on the request headers and the mock service which implements bad behavior.

Conclusion

Null Object Pattern is absolutely must have for well-scoped dependencies and at the same time could be a burden to implement for commodity ones as it would require a lot of engineering hours to implement.

Keep in mind that this pattern is just “hiding” the failures from the end user while others like circuit breaker, and health checks are actually trying to recover. So apply it when it’s necessary and don’t forget to set it up for each case when the null object pattern triggers.

Further reading

Top comments (4)

Collapse
 
helios_solution profile image
Helios Solutions

I just read your article on using the Null Object pattern to improve user experience in microservices, and I must say it's truly insightful and inspiring! Your explanation of how the pattern can eliminate unexpected null pointer exceptions and provide a smoother user experience is spot on. It's incredible how such a simple design approach can have such a profound impact. Thank you for sharing this valuable insight and shedding light on how we can enhance our microservices architecture. Keep up the fantastic work!

Collapse
 
mati365 profile image
Mateusz Bagiński

Why not just use Optional?

Collapse
 
frolk profile image
Frol K.

Thanks for the question Mateusz
It's ok to do so but I see a couple of reasons on top of my mind why not to:

  1. It's a classical problem of distinguishing between empty value and the case when you weren't able to retrieve. This could mess up business logics.
  2. Making the fields nullable you loose the advantage of static typing, code becomes less readable and prone to errors
Collapse
 
mati365 profile image
Mateusz Bagiński

I mean Optional monad