Roller Coaster Design Decisions (Part 2)

Roller Coaster - Design Decisions

This section will deal with system designs, SOLID, API considerations, cross cutting concerns, unit testing, and integration testing.

System Designs

I’ll work though different designs and find the design that fits my principles best.

(1) Monolith

A single project and database.

Pros

Cons

(2) Monolith with Isolated Schemas

A single project and database with isolated data access using schemas.

Pros

Cons

(3) Monolith with Isolated Databases

A single project with multiple databases and a reporting API that contains non sensitive data.

Pros

Cons

(4) Microservices with Isolated Databases

Multiple projects with their own database and a reporting API that contains non sensitive data.

Pros

Cons

(5) Microservices with Isolated Databases, Bus (Pub/Sub), and SignalR

Multiple projects with their own database and a reporting API that contains non sensitive data. Communication between APIS is done though a bus via a pub/sub modal using SignalR for signaling. APIS can push to the user using SignalR.

Pros

Cons

Additional System Design Considerations

SQL / NoSQL

I have limited experience using CosmosDB (noSQL) professionally. But I wanted to consider the pros and cons and see if noSQL was a good fit.

Pros

Cons

The pros look fantastic. I have concerns being able to query for reporting purposes but design 3, 4, and 5 solves for that. The inability to query and complexity of the records for the coasters API do not seem worth the effort. I hope one day to improve my noSQL skills further but It does not appear to be a good fit for this project.

Redis

All designs except for (1) have databases that require you to go through the API to access it. Additionally, I intend to keep the database and their API on the same machine. With these requirements the benefits of Redis became greatly diminished. If I find I have queries taking a long time or load concerns on my database I will reconsider Redis in the future.

Design Conclusion

After reviewing pros and cons of each design and then comparing them to my principles a clear winner stands out. I am going to go ahead with microservices with isolated databases, bus (Pub/Sub), and SignalR. The durability of the design, ability to maintain, and flexibility it brings are worth increased responses times, complexity, additional work and hosting costs.

SOLID

SOLID helps keep applications maintainable and testable. These traits fit very well with my principles.

An implementation detail of SOLID that needs consideration is dependency inversion. This dictates that dependencies should depend upon abstractions instead of concrete classes. This brings the benefits of classes being extensible and testable. To solve for this generally factories or dependency injection are used.

Factories are methods that generate in instance of a class. Dependency injection (DI) uses configuration, and injects the dependency where you need them. In reality DI injects them in more places then where you ask for your dependency but only as needed.

I have chosen to use dependency injection because it reduces code, reduces tests, reduces scope and has additional extensibility options.

API Considerations

Each API will include the following projects

The two interesting points here are database and proxy. Keeping all of my database queries inside of source control has served me well in the past. The proxy for most APIS won’t be used in production, but will give me another option to test my API when running locally, and when running integration tests.

Each project (excluding database and proxy runner) will have a unit test project with them.

Cross Cutting Concerns (NuGet Packages)

Now that I have principles, design and API considerations I have gone ahead and created a flow here. In this example, I can see that APIS will be using REST, SQL, and redacted logging.

High level Flow

Before taking my theory too far, I decided it was time to create a quick prototype of Account API and flush out unit tests. Doing so turned up concerns about testability and repeating patterns of code. Here are 2 of my main prototype flows.

After reviewing my prototype, I came up with these cross-cutting concerns.

Versioning

After reading https://devblogs.microsoft.com/devops/versioning-nuget-packages-cd-1/ from Microsoft I decided to follow their lead. All of the packages will follow semantic versioning. For development Ill add CI + Datetime to the version.

Abstractions

At my place of work, we have a much larger stack for our use cases, and there is strong coupling between packages. I reviewed solutions to this and found Microsoft solves this by creating abstraction packages. I will add them as needed.

Unit Testing

I have discussed with many developers the pros of cons of unit testing and have made my own conclusions.

Pros

Cons

There are different opinions on how to unit testing and what should be tested. These are mine.

I have written unit tests on the daily for 12 months. I have found the process of writing units to be even more valuable than the tests themselves. It forces me to slow down and take heavy considerations of my code by walking through all the of code paths without glossing over anything. I also enjoy the increased confidence I have when modifying an existing solution as breaks existing tests pointing me to places that I caused a change.

Integration Testing

With all APIs having a proxy built with them, integration testing should be an easy project to maintain. Integration tests will call all APIs and look for all expected returns except for server errors.

Conclusion

After careful thought on multiple system designs a plan emerged that fits well for the user stories and principles. Next walking though implementation decisions with SOILD using dependency injection. Then creating a general guideline for APIS using N-Tier, proxies, and SQL Scripts. Then looking though request flows and prototyping to find cross cutting concerns. Finally, considerations with unit and integration testing were reviewed.

These considerations have help set the table to hit the ground running with a clear high-level plan. I have heard that designing too early can cause over-architecting instead of growing it as you need it. I have found that by the time it’s a major problem it can be a massive effort and high level of risk to change it. Explaining to your boss that you need take a few days, weeks, or months to rewrite code for maintenance is an uphill battle.