Book Review?: Team Topologies

on Monday, December 30, 2019

Matthew Skelton created a blog post/website describing team topologies a few years ago, and with the help of others has created a book from it (Amazon, Audible).

The book breaks down the teams into 4 architypes (supplemental materials at itrevolution.com):

  • Stream Aligned Team
  • Enabling Team
  • Complex Subsystem Team
  • Platform Team

And 3 Interaction Modes:

  • Collaboration
  • X-as-a-Service
  • Facilitating

The book will help describe the team types and what their usage patterns are.

I ran across the website which shows the Anti-Patterns and Patterns of Team Topologies about a year ago. I was trying to answer questions from coworkers about “What should our teams look like if we do DevOps”, and the website left me with a lot of guess work into the details of each of the patterns. I don’t think the original intent of the website was to be confusing, but it explored an area that I wanted to know more about so it left me with more questions than answers. And, this book didn’t seem to directly answer those questions.

It seemed to expand on the original work with new ideas and new thoughtful constructs. The team architypes lined up well with what I had seen in my own work and also lined up well with my prior understanding of DevOps / Lean Project Management methodologies. All of which are well described in the books from itrevolution.com and other publishers. (This is not an advertisement, these books have well established and useful knowledge within them.)

For the most part, the Stream Aligned Teams make a lot of sense to me because I have seen working example of them. At work, we have multiple teams which sit within distinct departments and work on the applications/projects/products for those departments.

However, the Enabling and Platform teams seemed like they could be much more intertwined than what the book described. I personally feel like I work within a platform team, but the platforms that I help provide are only useful if (1) they are developed in collaboration with a Stream Aligned Team and (2) the platform product in demonstrated/shared/knowledge-transferred to all Stream Aligned Teams. To me, #2 seems like the function of an Enabling Team. So, it feels like a company that wants to establish a permanent Enabling team would need to contain members that were in constant rotation with the Platform teams in order to:

  • Keep Enabling Team members up to date on the latest utilities developed by the Platform teams
  • Keep the Enabling Team members skills sharp on implementation details of the Platform team products
  • Allow for Enabling Team members to develop new functionality into the Platform without having to cross a communication/trust boundary (fighting Conway’s Law)

I think that the Enabling and Platform teams might have been separated in the book due to the need for the Enabling team members to have a higher degree of interpersonal communication and collaborative skills. To be very blunt, the Silicon Valley architypes of system administrators didn’t come out of thin air (1, 2) and Platform teams would need good System Administrators.

So, maybe it’s the more evangelist personality types of the Platform Teams that can rotate into the Enabling Teams.

The only team type that I never fully connected with was the Complex Subsystem Team. I thought of some potential example projects which could fit the description. But the teams were adhoc and my example cases never felt quite right. Maybe I just don’t work someplace that has difficult enough problems to require such teams. The book is very clear that these teams are optional and would not be needed for all work environments.

In the end, I think the book adds to the overall body of literature on DevOps, and it can really help a group if they are scratching their heads about what sort of team structure could help improve their organizational structure.

Update .NET Core Runtime Installer for SDK

on Monday, December 23, 2019

A while back, I wrote a post titled Monitor and Download Latest .NET Core Installer. The post was about a script which could be used to monitor the aspnet/aspnetcore releases page, and if a new releases came out the installer for the .NET Hosting Bundle would be downloaded. A piece of code I didn’t include in that post, was a script that then took the downloaded .exe installer and created an installation package from it. This installation package was targeted at servers that host ASP.NET Core web applications. This post is not about that secondary script.

Instead, this is a small tweak to the monitor/download script. Instead of downloading the .NET Hosting Bundling, it would download the SDK. And the installation package that it eventually creates (not included in this post) is targeted at build servers.

Create a Custom ProblemDetailsFactory

on Monday, December 16, 2019

An Exception Handler is also needed for this to work. You can read more within the follow-up post, ExceptionHandler Needed.

In .NET Core 2.2/3.0 the ASP.NET Team made a move towards a validation and error reporting standard called Problem Details (RFC 7807). I don’t know much about the history of the standard except for what’s listed on it’s description. It became a proposed standard in March 2016 (which means it was probably being developed for years before that), and it was sponsored by M. Nottingham (W3C Technical Architecture Group), Akamai (they’re pretty famous) and E. Wilde (Siemens).

This standardization also lines up with something David Fowler has been talking about for a little while (1, 2, 3), Distributed Systems Tracing. From an outsiders perspective it really feels like many of the teams at Microsoft are trying their best to get more observability, metrics, and tracing into their tooling. And Microsoft seems to be using the “extensions” described in the Problem Details RFC to add a new property called “traceId”. I think this property will line up with a larger effort by Microsoft to support OpenTelemetery (Microsoft reference) and potential improvements to Application Insights.

So … Microsoft has these great baseline ProblemDetail objects which help standardize the way 500 and 400 errors are returned from Web APIs. But, how can you extend upon their work to add some customizations that are particular to your needs?

Well, when you read the Microsoft Handle errors in ASP.NET Core web APIs documentation, you feel like it must be pretty easy because they say you just need to “Implement ProblemDetailsFactory”. But, that’s all the documentation does. It just “says” you should implement it, there is no example code to work from. The example that is given shows how to replace the default factory with your custom factory (which is a great example, Thank You!), but there’s no example given on what your factory could look like.

This leads to the question of “How does Microsoft do it?”. Well … they use an internal (non-public) DefaultProblemDetailsFactory.

It would be great if DefaultProblemDetailsFactory could be made public.

One of the striking features of that default implementation it never references System.Exception. It’s job is to translate an uncaught exception into a 500 Internal Server Error response object. But, it never uses an exception object in it’s code?

Maybe that’s because it does the translation earlier on the process, like in ProblemDetailsClientErrorFactory. I really don’t know how it all connects. The original developers are some pretty smart people to get it all working.

Anyways … for this example, I’m going to:

  • Use the DefaultProblemDetailsFactory as the starting code to extend.
  • Create a custom class which the Factory will look for in order to alter the returned ProblemDetails object.
  • Use a Feature on the httpContext to pull in Exception information (I don’t know how else to get the exception object?)
  • Use the ProblemDetailsFactoryTest class from Microsoft to help build a unit test.
  • Update the unit test to inject the exception.

Let’s start with the custom class (YourBussException.cs) that will be used by our custom factory to extend the returned data. The class will:

  • Use the underlying Exception’s Message property to fill in the “Detail” property of the ProblemDetail object.
  • Add an ExtendedInfo object where your development teams can add extra information that can help inform API clients on how to resolve the issue.

Next we'll make some small updates to the factory in order to create one that will translate our exception into a 400 Bad Request response (YourBussProblemDetailsFactory.cs):

Alternatively, you can use a Decorator Pattern to wrap the default InvalidModelStateFactory as described in AspNetCore.Docs/issue/12157, How to log automatic 400 responses on model validation errors, option #2 (using PostConfigure<>). The concern I have with this approach is that you are no longer using the Dependency Injection system to create your factory. You are hand creating an instance of the factory and that instance is no longer easily referenceable to any code that wants to interact with it. This also makes the code more brittle to changes and less testable.

Finally, we can use the example code from Microsoft Handle errors in ASP.NET Core web APIs documentation, to swap in our new YourBussProblemDetailsFactory (Startup.cs):

Now, you should be able to throw your exception from anywhere in the code and have it translated back as a 400 error:

Some things to take note of:

  • The ProblemDetails classes were introduced in ASP.NET Core 3.0. So, you’ll have to update your target framework to ‘aspnetcore3.0’ or above to make this work.
  • You’ll also need to add in a FrameworkReference to ‘Microsoft.AspNetCore.App’ as the Microsoft.AspNetCore.Mvc.Infrastructure namespace only exists within it. And you can only get that reference through the FrameworkReference (as opposed to nuget packages). See Migrate from ASP.NET Core 2.2 to 3.0 for an example.
  • The null-coalescing operator (??=) only compiles in C# 8.0. So, if your project, or your referenced projects depend on ‘netstandard2.0’ or ‘netcoreapp2.X’ then you’ll need to update them to get the compiler to work (this took a while to figure out.) (<--That’s right, your referenced projects have to update too; it’s really non-intuitive.)

Finally, let’s take a look at a unit test. I’m going to make this code sample a bit short. To make the full example work, you will need to copy all of these internal classes into your testing code:

This is the code snippet needed just for the test (YourBussProblemDetailsFactoryTests.cs):

An Exception Handler is also needed for this to work. You can read more within the follow-up post, ExceptionHandler Needed.

Using Swagger UI and ReDoc in ASP.NET Core 3.0

on Monday, December 9, 2019

I was working with the Swashbuckle.AspNetCore library and wanted to play around with both the Swagger-UI active documentation endpoint and the ReDoc active documentation endpoint at the same time. I wanted to compare the two to see which was easier to use. (It turns out they’re both really easy to use.)

But, I ran into a problem, both UI endpoints wanted to use OpenAPI json document to pull in the Web APIs definition. And, the default configuration that comes with Swashbuckle.AspnetCore will generate that document at “swagger/v1/swagger.json”. (You can change this path using the .RouteTemplate property during configuration.)

https://your.domain.com/subapp/swagger/v1/swagger.json

This wasn’t a problem with the Swagger-UI system because the .UseSwaggerUI configuration has a default base path of “swagger”. This means all you need to do to configure is call .SwaggerEndpoint(“v1/swagger.json”, “My API V1”) and it will find the correct path.

Swagger-UI’s default path is “swagger”, so adding “v1/swagger.json” will create “swagger/v1/swagger.json”.

However, the .UseReDoc configuration has a base path of “api-docs”. Which means you shouldn’t be able to find the default json documentation path under it.

You shouldn’t be able to … but in a very interesting twist, the configuration does allow for relative pathing. Which means you can use “..” to escape the default pathing. This was great! So, you can use a relative path, “../swagger/v1/swagger.json” to reference it.

ReDocs default path is “api-docs”, so you can escape it using relative path like “../swagger/v1/swagger.json”. This will create the expected “swagger/v1/swagger.json”

Here’s an example:

Getting Root / Base Path in ASP.NET Core 3.0 (IIS)

on Monday, December 2, 2019

In my mind a root / base path for an IIS application looks something like this:

https://my.host.com/some/subdir

That format contains the schema://host/base-path

For most web applications, this information isn’t important. And, for the most part, you should try to develop your applications to be agnostic to where it’s hosted. To do this you can use techniques like always using relative pathing in your web applications links (<a href=”../something/relative.html”>) and web api results. Of course, if you know a link is pointing to a resource outside of your application, then using an absolute path would make sense.

However, using relative paths isn’t always possible and sometimes you will need to create an absolute path for your local application. I’ve run into this scenario when a nuget package or submodule wasn’t designed with relative paths in mind and they require an absolute Uri/path as input.

When I googled for how to get the base path within an Asp.Net Core 3.0 application today, the top results where focused on getting the base path of a request. Which is really easy. The HttpRequest/Message will contain that information and it’s always at your fingertips. But the scenario I had in mind was during Startup.cs, specifically during the .Configure(IApplicationBuilder app, IWebHostEnvironment env) method.

There is a good stackoverflow post, How to get base url wihtout accessing a request, which demonstrates how to get the information in ASP.NET Core 2.X. It showed that there is an internal feature called, IServerAddressesFeature, which contains the full root / base path information (example at the start of the post). In ASP.NET Core 2.X, that service/feature was accessible from the IWebHost object that could be found in the Program.cs class.

In 3.0, IWebHost has been replaced by IHost and the .ServerFeatures property is not available on it. But, that’s because it’s even easier to get ahold of. When the IHost.Run() method is called in Program.cs, it will (eventually) call Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env). And, the ServerFeatures property is exposed on the IApplicationBuilder object. So, you can retrieve the IServerAddressesFeature information directly within the .Configure method.

For example:

And a full example:


Creative Commons License
This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.