Testing WITHOUT Mocks or Interfaces!

A common approach people take with testing is mocking. Specifically defining interfaces for dependencies which are then typically mocked so you can test in isolation. While interfaces can be helpful for mocking as well as fakes and stubs, there can be other approaches taken. Meaning you don’t need to create an interface for everything.

YouTube

Check out my YouTube channel, where I post all kinds of content accompanying my posts, including this video showing everything in this post.

Deterministic

Let’s start with a method in an OrderService for creating an Order.

To test this method, there are a few dependencies involved. The OrderRepository, ItemRepository, URIComposer. Here’s what the test might look like by using a mocking library.

The problem is, as I’ve described above, is this test is flaky. That’s because the OrderDate is being set by DateTime.UtcNow. That’s non-deterministic.

Sure, we could be more lenient on our assert by maybe using a small range/window, but ultimately we want the result to be deterministic.

Interfaces

You could jump directly to an interface, which I’ve seen quite a bit of for this exact usage case with DateTime.

With the appropriate registration with the ServiceCollection, we can now inject a ISystemDateTime instead of calling DateTime.UtcNow.

We can be deterministic within our test by returning a specific DateTime for UtcNow.

Function

If you have a class/interface with one method, you have a function. The ISystemDateTime is exactly that. We have more options than interfaces when it comes to abstractions. In this case, using a delegate is also an option.

Again, by registering this delegate and the static method implementation, we can inject that delegate rather than an interface.

Our test becomes less cumbersome as we can easily create a stub for returning a deterministic DateTime without additional libraries or dependencies.

Values

As we break this down, you might also wonder why we even inject some abstraction when creating the order but instead pass the value of the DateTime to the order creation—basically moving up the call stack.

Now the caller is responsible for passing a DateTime to the CreateOrder, simplifying the test even more as we no longer have a dependency to pass to the OrderService

Abstract Classes

Have you ever needed to test an implementation that needed to use HttpClient? If so, you’re faced with the same issue where you want a deterministic result.

First, here’s an example of an ExchangeRateClient for getting currency exchange rates.

HttpClient doesn’t have an interface. So how do you test? Well, it does support providing your implementation of an HttpMessageHandler, where you must implement the SendAsync method.

Abstractions

You don’t always need to default to interfaces. You have other abstractions like delegates and abstract classes, and changing your design to move non-deterministic calls up the call stack so you can pass values instead.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

The Challenge of Microservices: UI Composition

One of the most challenging aspects of distributed applications like microservices is UI Composition. How to combine all the data from various services for your UI. I’m going over a few different solutions and their pros and cons for UI Composition and ViewModel Composition.

YouTube

Check out my YouTube channel, where I post all kinds of content accompanying my posts, including this video showing everything in this post.

Multiple Sources

When you’re working within a monolith and a single database, we don’t have the issue of compositing data. We get all the data we need from the single source database.

As an example, here’s a typical e-commerce website with a listing of products.

Products Listing

The client could be a browser, and the app could be doing server-side rendering, or the client could be a JS/SPA frontend, and our App is an HTTP API.

Either way, our app can get all the data from a single database and compose and return the data as needed. It can get the product names, images, and prices from the same database.

Single Database

The challenge is when multiple services are involved, each with its own database. Each service owns its own set of business capabilities and the data behind it. For example, if we had a Catalog service that owns all the product names and images. The Sales service owns the product price. How do you compose all that data together from two different services?

There are a few different options. The first is doing UI Composition.

UI Composition

UI Composition is about having a logical boundary own UI components that are composed to make your UI. A component (or set of components) would be responsible for getting the relevant data it requires.

The catalog service would own a Brand and Type filtering component. It would also own a Product display component to show the image and product name.

UI Composition

However, The price is owned by the Sales service and it would own a component for displaying the price.

With some pseudo-code, this is what the UI might look like.

UI Composition Code

We’re iterating over a list of products and then displaying the image, and name and also using the product price component.

This can work in a lot of situations however, in a grid/list, as you might have guessed, we just turned this into an N+1 problem. For each product, our price component is going to have to make an HTTP request to the sales service to get the price for that individual product.

UI Composition

This is not ideal at all. As mentioned, this can work in different situations, but not when we have a lot of components, nested components, or N+1 situations.

ViewModel Composition

Another option is using ViewModel composition. Typically this is done with a Backend-for-Frontend (BFF), which will call the relevant services and compose the data to be returned to the client.

The BFF would call the catalog service to get all the products to display.

ViewModel Composition

Then it would make a single call to Sales requesting the price for only the products it received from the catalog. We’ve removed the N+1 issue as we’re now requesting all the prices in a single request so that we can compose all the relevant data and return it to the client.

Here’s an example of what the composed data being returned to our client would look like.

ViewModel Composition JSON

You’re not limited to doing UI Composition or ViewModel composition. You can do both. Use the method that makes the most sense in a given situation.

Sorting/Paging/Filtering

The biggest issue people run into with ViewModel composition is needing the ability to sort, page, and filter a listing of items.

In the example of our product listing, if we were sorting by name, it would work totally fine. However, if we’re sorting by price, our current flow would not work. We were making our initial call to the Catalog service, then getting the prices for all the items. Now we would have to reverse that flow so that got all the SKUs from the Sales service are sorted by price, then call the catalog service to get the rest of the information.

While this isn’t overly complicated in this exact example, if you have more than two services and/or you want to sort and filter. For example, you want to filter by product type and sort by price.

Event Carried State Transfer

If you have all the data required in a single service, then you don’t have to worry about these types of sorting/paging/filtering issues with multiple services.

This is why event-carried state transfer is popular to distribute data as a local cache to other services.

Before I explain how it works, I must say that I’m not a fan of distributing data to other services. If you’re doing so with reference data, such as my example for query purposes (not commands), it can work. However, I do not recommend distributing transactional data around. Check out my post Event Carried State Transfer: Keep a local cache!

The way event-carried transfer works is by publishing events of state changes so other services can consume these events and keep a local cache copy of the data from another service. Again this should be non-volatile reference data.

When a catalog item is updated, let’s say the name changed, a ProductChanged event would be published.

Event Carried State Transfer

The sales service would consume this message, which contains the ProductId (SKU) and new product name. It updates its local database of this catalog information. It’s essentially a local cache of catalog data.

Event Carried State Transfer

With this local cache, the sales service has all the data required to compose the product listing and perform any filtering, paging, and sorting.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

What’s enough Complexity for a Domain Model?

Should you be applying domain-driven design? Do you need a domain model? You might want to if you have a lot of complexity within your domain. I often get asked, “what is a lot of complexity?”. I’m going to provide an example by uncovering more and more complexity within a domain. And give a few different insights that should help you make the decision.

YouTube

Check out my YouTube channel, where I post all kinds of content accompanying my posts, including this video showing everything in this post.

Mounting Complexity

Through this post, I’m going to illustrate an example using a shipment, moving freight/goods from one location to another. Everyone should be able to relate to this. You order something online and have it delivered to you.

Let’s start with the simplest form. First, a vehicle will arrive at the first stop, often called the shipper, and pick up the freight.

No Complexity: Pickup

Then the vehicle will drive to the destination, often called the consignee, and deliver the freight.

No Complexity: Delivery

When considering an order you place online, this would mean a vehicle goes and picks up your order/package at a warehouse or store, then delivers it to your house/apartment.

No Complexity: Model

On the surface, the complexity within this domain model might seem minimal. A shipment will be associated with a vehicle, pickup, and delivery location. In terms of behaviors, we would be able to arrive at the pickup location, pick up the freight, depart the pickup location, arrive at the delivery location, and deliver.

This is pretty simple at this point in terms of our conceptual model. In terms of domain logic, it would be around making sure we arrive at our pickup location before we arrive at the delivery. This means we are doing shipment steps in the correct order.

More Complexity

As we dig into the reality of this domain, the complexity within this domain model is about to go up drastically. A vehicle isn’t always going to pick up a single package and bring it directly to the delivery location. What may also occur is bringing the shipment to a warehouse before the final delivery.

More Complexity: pickup

This means that Vehicle A might pick up the freight from our pickup location and then deliver it to a warehouse or some intermediate location.

More Complexity: Warehouse Delivery

The freight might sit at this warehouse location for a period of time.

More Complexity: Warehouse

Finally, when the product needs to move to the delivery location, we could have Vehicle B arrive at the warehouse to pick up the freight.

More Complexity: warehouse pickup

Vehicle B then drives to our delivery location to deliver the freight to the final destination.

More Complexity: delivery

We’ve added a bit more complexity. Now we need to keep track of which vehicles are involved in our shipment.

More Complexity Model

Now with this model, we could have many intermediate pickups and deliveries as needed. For example, we may have two different warehouses involved before our final destination.

More Complexity

Now let’s say that we have a shipment that the freight is split into multiple packages that need to be delivered to multiple locations.

First, the vehicle picks up multiple packages at the pickup location.

Even More Complexity: pickup

It then brings the freight to the first delivery location and delivers one of the packages.

Even More Complexity: Delivery #1

Then it drives to the other delivery location to deliver the final package.

Even More Complexity: Delivery #2

Now, we need to keep track of the freight itself to determine which delivery location it belongs to.

Even More Complexity: Model

Complexity. It never ends.

Let’s combine those last two examples: a warehouse or intermediate stop and multiple delivery locations.

Endless Complexity

Now depending on the size of freight and the types of vehicles, it’s not very efficient to have one shipment per vehicle. So this means that we have multiple shipments on a vehicle. And those individual shipments may have multiple pickup and delivery locations.

Is this starting to sound complex yet? Modeling the data might not be the most challenging part. However, all the business logic of making sure you’re routing these stops in the most efficient order, making sure you’re going to be on on-time for pickup and delivery times required, can get incredibly complex.

The reality of this domain has a lot of complexity that I’m just scratching the surface of. The point is that what looks pretty simplistic on the surface of a single package with a single pickup location and delivery location, in reality, goes much deeper and has a lot of complexity you need to manage.

What’s enough Complexity?

Do you warrant a domain model to manage the complexity? Here are a few questions to ask yourself.

Is it CRUD (create, read, update, delete)? Honestly, is it CRUD? When you’re working with a system that’s primarily CRUD, all of the workflow and business processes are in the end-users heads. They are the ones executing the workflow and entering the various pieces of data. They decide if X happens, then they need to input data into Y. The end user understands the workflow, not the software.

If you’re building a system that captures the workflow and will contain all that business logic, then yes, creating a domain model can be very beneficial as a way of organizing all that logic and complexity.

How many capabilities does your system provide? If you’re trying to decide if you should capture the workflow in your software, how many capabilities does it need to provide? If it’s minimal, it might not be needed.

If it is very limited, will it grow? Will the number of capabilities grow over time? It may start very small, but as its value is realized, more and more features are added.

The example I gave at the beginning may be the 80% use case. In that case, maybe you don’t need a domain model. But you do for the other 20%, which has a lot more complexity.

Not all models need to solve all the problems.

If you don’t know if you have a lot of complexity, then you don’t know enough about the domain and the problem the system is trying to solve. If this is the case, explore more about the domain, the problem space, and what it is you’re trying to solve.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design