Publishing Events from CRUD or Commands?

There are many different ways to create events in Event Driven Architecture. Events from CRUD or created from specific actions that have occurred. In some cases, consumers only care about the full scope of an entity/data. In other boundaries, consumers only care about what has occurred. When publishing events from CRUD you’ll likely care more about propagating state. However, events derived from tasks/commands are often used more as notifications about business process occurrences.

YouTube

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

CRUD

When developers start getting into Event Drive Architecture, I find they most often create events that are represented by Create/Update/Delete of an Entity. Meaning if there is a Product Entity, then they have a ProductCreated event, ProductUpdated event, or ProductDeleted event.

This is most often driven by UI and APIs that are CRUD and Entity oriented. I call these APIs Entity Services and try to avoid them when working in the core of a domain. For more info on Entity Services, check out my post AVOID Entity Services by Focusing on Capabilities

The next most common type of event created for those new to Event Driven Architecture is still driven by CRUD and Entities, but all the updates are property-based. If the Product Entity has a Quantity property, then the event published ends up being a ProductQuantityUpdated event.

In both types of events what’s common about them is the event contains data. If it’s a Created or Update type event, often it will contain the entire Entity that was created. If it’s a Property Updated event, it will likely contain the new value of the property, but it’s also common that people will put the old value as well.

The reason why this is common is that a lot of systems are developed primarily based on CRUD. If you’re UI and APIs are driven by CRUD, you’re going to develop events that are derived from CRUD.

Behavioral

What’s the alternative to CRUD? Taking a more task-driven approach to developing a system. Providing end-users with capabilities that are directly related to the business functions they perform. In my example above with ha Product Entity, a user doesn’t “Update the Quantity”. There is a reason for this that’s related to a task. Did the warehouse do a stock count and realized they found more products on the actual shelves than are stated in the system? If so they would perform an “Inventory Adjustment”. There’s a difference between “Inventory Adjustment” and “Update the Quantity”.

In the events above, they are all behavioral. These events are statements of facts of actual business events. In order to publish these types of events, you need to move away from CRUD and move towards Commands and a Task Based-UI. For more on this check out my post Decomposing CRUD to a Task Based UI.

In the above screenshot, we’ve moved away from CRUD in some aspects. The Name and Description can be saved by a simple update however we can no longer just change the Quantity. We need to specify an action/task that is a business reason why we are changing the Quantity.

From this, we can derive that we are creating and publishing an InventoryAdjusted event which allows us to be much more explicit.

Explicit vs Implicit

CRUD-based events (ProductCreated, ProductUpdated, etc) aren’t inherently bad. They can be useful for referential data that lives on the edge of your system. Data that is not a part of the (sub)domain you’re focusing on. For this reason, you might publish CRUD-based type events because other service boundaries want a local cache copy of that data. However, since they aren’t explicit about the reason or business task that occurred and created the event, you won’t have that insight from a consumer. You would have to imply it, if you could, based on data that changed.

Behavioral Events (ProductShipped, ProductInventoryAdjusted, etc) are explicit. They tell consumers exactly what has occurred. These types of events are generally used as a notification to other service boundaries that might be a part of a long-running business process. They usually only contain a minimal amount of data. They are generally used for data propagation for local cache copies. Check out my post on Event Choreography & Orchestration that covers how behavioral events are used between services.

Source Code

Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.

Related Links

Follow @CodeOpinion on Twitter

Software Architeture & Design

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

Handling HTTP API Errors with Problem Details

If you’re developing an HTTP API, having a consistent way of returning errors can make handling errors on the client-side simpler. I’ve talked about Problem Details in another video, which is a standard for returning machine-readable details of errors in an HTTP Response. In this video, I’ll cover how to consume those HTTP API Errors from the client-side.

YouTube

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

Problem Details

If you’re unfamiliar, Problem Details (https://tools.ietf.org/html/rfc7807) is a standard for providing errors from your HTTP API to consuming clients.

HTTP [RFC7230] status codes are sometimes not sufficient to convey enough information about an error to be helpful. While humans behind Web browsers can be informed about the nature of the problem with an HTML [W3C.REC-html5-20141028] response body, non-human consumers of so-called “HTTP APIs” are usually not. This specification defines simple JSON [RFC7159] and XML [W3C.REC-xml-20081126] document formats to suit this purpose. They are designed to be reused by HTTP APIs, which can identify distinct “problem types” specific to their needs. Thus, API clients can be informed of both the high-level error class (using the status code) and the finer-grained details of the problem (using one of these formats).

If you’re interested in how to produce Problem Details from your HTTP API, check out my other post: Problem Details for Better REST HTTP API Errors.

For the rest of this post, I’m going to use a simple ASP.NET Core route that is returning a problem details response that we’ll interact with.

Here’s a sample of what a Problem Details response body looks like. It will also have a Content-Type header of application/problem+json;

Consumer

To illustrate handling a Problem Details response in C#, we can provide the HttpClient with a custom HttpMessageHandler. This will allow us to inspect the response and check if the Content-Type response is of application/problem+json, and if it is, throw a ProblemDetailsException with the relevant properties.

Now when using an HttpClient, we can pass it our ProblemDetailsHttpMessageHandler that will throw and we can handle that specific exception.

When I run this sample console application, here’s the output from all the Console.Writeline.

Extensions

The real power for consuming Problem Details as HTTP API Errors is in the Type property and Extensions. The Type property in the Problem Details response is a URI that should provide developers at design time information about the error. This can include additional properties, called extensions, that may also exist in the response body. In my example above, “invalidParams” was an extension. My client code, albeit trivial, was looking for that specific type so that it could deserialize the response to a specific validation problem details type that I created, that was fully aware of that “invalidParams” extension.

If you leverage the Type property you can build very specific error types using extensions that your client can be aware of to provide a better experience.

Javascript & Typescript

Alex Zeitler created a nice package for parsing Problem Details that you can use within a Javascript / Typescript client. Check out his package http-problem-details-parser.

Here’s a sample using a mapper for the invalidParams extension.

Source Code

Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.

Related Links

Follow @CodeOpinion on Twitter

Software Architeture & Design

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

Multi-tenant Architecture for SaaS

When building Software as a Service (SaaS) you’ll often need to use a Multi-tenant Architecture. There are many different ways that you can segregate compute and data storage in a multi-tenant architecture. Data storage can be in a silo or partitioned. Compute can be pooled or siloed. And both together you can create lanes for groups of tenants. In this architecture, having the identity of each request is critical in being able to route a request all the way through to the right services and resources.

YouTube

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

Single-tenant Architecture

First, let’s talk about single-tenant architecture. This doesn’t mean it’s not a multi-user environment, just that all users belong to the same tenant or organization.

The App (Compute) refers to the compute to run your application. This could be load balanced or running as multiple instances on multiple nodes. The DB (Storage) is your database and any other infrastructure dependencies such as caches, etc.

With this type of topology, if you need to move to a multi-tenant architecture, the most common option is to replicate the compute and storage for every new tenant.

This means each tenant has its own compute and own storage.

The advantages are that each tenant is completely siloed. They share nothing. There is no concern for data being exposed to the wrong tenant. Each tenant can be scaled independently. There are no “noisy neighbors”, meaning one tenant can’t hog or over-consuming compute resources affecting other tenants.

The disadvantages are managing more infrastructure, which includes deploying the application and related changes storage. The cost of potentially having more compute and storage than are required by a tenant. If a tenant is barely using the application and only consuming 5% of the compute, you’re just wasting resources and still having to pay the cost of those resources.

Multi-tenant Architecture

The next logical step for most is to share compute but keep storage separate. This allows you to silo data to prevent exposing data to the wrong tenant but sharing compute resources.

This allows you to maximize the compute usage and scale when necessary but keeps data separate. In order to achieve this, your application must now be aware of the tenant making the request o the application so you can route to the correct database/storage.

Another option is sharing compute and storage.

In this situation, the usage of compute and your storage is maximized. However, this means that data is no longer siloed. This means you must use a partition key in your schema tables or documents to indicate which tenant the data belongs to. In a relational database, this means adding a TenantId to your tables.

In the example above, the first and third rows belong to Tenant1. When performing any queries to the database you must be filtering by the TenantId.

Identity

If you’re sharing compute or storage and using a partition key, you must be aware of who is making the request to your application so you can route to either the correct database or use the partition key. Identity is important to manage this.

When you have a user login to your application through whatever means, the token it returns to the client/user must contain some identifier of the Tenant that user belongs to.

Then any requests made to the application will pass that token containing the TenantId. This can then be used in any queries to make sure it’s filtering and selecting the data for that tenant.

Multi-tenant Architecture for SaaS

If the database was siloed then it would also use the TenantId in the token to determine which schema or database instance to use.

Multi-tenant Architecture for SaaS

As an example of what that looks like using Entity Framework Core is u can use the HasQueryFilter when building the model to have the DbContext automatically add the filter on every query.

There are many different ways and techniques to do this so that when writing your application code, you don’t have to think much about having to write the actual filter. You don’t want to have to think about writing this logic at all. I recommend it being at a higher level or abstracted so when you’re writing application code, you feel like you are in a single-tenant architecture and not a multi-tenant architecture.

Hypermedia

Another option is using Hypermedia. If you’re developing an HTTP API then when your client app authenticates with the Identity service, it will also specify the host with which you’re interacting. Meaning it will tell the client at runtime which hosts it will be making the HTTP Requests to.

With hypermedia, you’re not building URIs but rather the server is providing them to you in responses. If you want more details on leveraging hypermedia check out my post on building Smarter Single Page Application with a REST API

If you’re developing a web app that’s server-side rendered, the same is applicable by redirecting the user to the correct host after login.

What this provides are different “lanes”. There can be many tenants that belong to a lane. Each lane has its own compute and storage. This is really like a mix and match of everything else described in this post.

Multi-tenant Architecture for SaaS

Lanes allow you some flexibility in how you deploy. You can deploy updates to a single lane as a canary deployment. You can deploy to a single lane, verify the changes are working correctly and everything is stable, and if so, deploy to more lanes.

Options

Hopefully, this illustrated that there are many ways to implement a multi-tenant architecture. You can fully silo and have a tenant have their own compute and storage. Tenants can share compute but still have siloed storage, or you can share both compute and storage with a partition key. With all these options, you can segregate further by adding lanes.

Source Code

Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.

Related Links

Follow @CodeOpinion on Twitter

Software Architeture & Design

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