I am going to show you how to use application architecture diagrams in a really, really easy way.
Now as we go through this, there are a lot of resources and a lot of interesting information on this topic on The Azure DevOps Podcast. So let’s get into architect or architecture diagrams. I want to make it easy and show you some diagrams that I use. You want to communicate what you want to either be built or what you are going to build with your teammates, then you need to draw something.
If it cannot be drawn, it cannot be built. Every profession that creates something has some type of design diagram. And so, we need that here.
Let’s pretend that we are starting a work order application. We need some diagrams that are going to communicate it. The OO diagram is going to be your bread and butter diagram; you could think of it as a class diagram. Also, we are going to make ample use of sequence diagrams. Then you might have some patterns, in the case of a work order, it has a status and it has a workflow. So, we need to make use of a state machine diagram. And a lot of times, an activity diagram will be interesting or an overall dependency diagram.
So let’s just take these and go through them one at a time to see how quickly we can use these particular diagrams to describe a work order application in a fashion where you would actually understand what .NET code needs to flow from this.
Application Architecture Dependency Diagram
Let us start with a dependency diagram. You know I am a fan of Onion Architecture. If I am laying out a visual studio solution, and it is going to be .Net, Blazor Web Assembly, C#, SQL server, and we are going to pull it up in Azure, one of the first things to think about is what it will look like.
So, I am just going to quickly spec it out not getting into explaining why. I am going to have a core library, which is .Net standard 2; and I’m going to have a UI project; also, I am going to have a data access project …
I am not going to worry about my handwriting. I’m just going to do this.
I know that I need some unit tests, so we’re going to have a unit test library, and I’m going to have some integration tests. These are going to be projects, so I need to have them here. Unit tests are going to be linked to core integration tests, and they are going to be linked to data, access, and core most of the time. And, if I put in acceptance tests, over here, most of the time they are linked to data, access, core, and UI. So that is our test coverage.
Now I did not put any arrows here, and that needs to be communicated. The core needs to have no other dependencies than .NET standard and your most stable libraries. You are only as stable as your most stable library. So, I am going to make sure that the UI, data access, and unit tests references core. I like that; all incoming references, no outgoing references for core. Alright, so that is our dependency diagram.
Class Diagram or Object Diagram (OO)
Now, what about our core object model or domain model? I love domain-driven design – the pattern and the language that it gives us. Let us just put the work order right here in the middle. This is going to be a class diagram or just an object diagram.
A work order does need a status. I need a status object, whether it is an enum or enumerated class. It will include statuses like “draft”, or “submitted”, or maybe “assigned” or “in progress”. Others may be “completed” and maybe “canceled”.
Also, I need a submitter, somebody who is going to submit the work order. Then we need someone to assign the work order, an assignee; and they could assign it to a technician, and the technician is also an employee. Indicate this inheritance relationship with an open triangle. And there may be a manager, who is an employee, and a technician has a manager; so I will add that. So, there is the object diagram, and taking that object diagram into consideration, let’s figure out how to put this application together.
Application Architecture Sequence Diagram
Let’s do a sequence diagram for just creating a new work order. It’s going to be in draft status starting out.
I am not going to go into UI patterns, I’m just going to be abstract for now and assume that the user is going to the screen where they can draft a work order. So, what happens?
Well, in the simplest sense, if we’re not really using any kind of scalable patterns, we might say that the UI uses the work order object directly and the controller, or the screen code, or whatever component, is just calling the constructor of the work order directly. And then, using the work order repository class, maybe the UI then goes directly ahead and saves this work order. And, of course, we have SQL server, and the workload repository is just doing an insert statement.
That could be a possibility, but that doesn’t scale at all because now we have to do all that for, the manager, technician, employee, etc. It just puts in too much logic and guarantees all of your logic, except for your SQL statements, are going to be right there in the user interface.
So, we do not like that. We want to do something a little bit better.
Let’s Talk About Drafting
Let’s consider an alternative, come up with a pattern, use a little bit from CQRS, draft some commands, and use a bus pattern.
I’m going to ignore the user interface for a little bit longer but still say the actor is going to draft the work order. Then instead of going directly to creating our aggregate root in our domain model, which would be work order, one of our entities that serves as a root of an aggregate, let’s instead craft a command where we pull away the user’s intent. The intent of the user, or the request of the user, is to draft a work order. I’m going to add the “draft work order command”.
So, we would create this command, similar to a DTO, and then we would pass it to a bus, or an IBus. This will look around for a request handler. If we are going to specify this type of pattern, then it is going to look for the right handler and say, “Hey, will you handle this command?”
Adding Classes to the Diagram
Okay, so how do we do that? We need a lot of classes that inherit from here, like “draft work order handler”. All your diagrams can be completely flexible; you can mix and match paradigms. The open triangle is “inherits from”, and through inversion of control, we are going to extend this, and it is going to implement the handle method.
Well, how does it do it? There is going to be a work order object, and this is the class that calls the constructor of the work order and sets status to draft. And now we need to save it.
So, we have a draft work order handler class that handles the command. Now in this handler, we can turn right back around and create some type of command to send back to the bus.
I will add save-entity or save-aggregate command, and after creating that command, pass in the work order to this save-aggregate command. Now, I get the object back and then I pass bus.send, passing this save-aggregate command. Now I have another command that you need to handle; you need to route.
We go right back to our abstraction and tell it, “I need to save an object.” In this case, a work order would probably, you know, if you have I entity or some type of interface you would do an inheritance relationship there, but let’s just say we have a save aggregation handler that inherits from IRequest handler.
So, in this case, it would implement the handle method. Then we would add the SQL server, and we would have an insert statement.
Okay. So that is how we can design a sophisticated application and after you design this, every one of these would then be on the docket for development tasks. We need the draft work order command. We need the Ibus. We need the IRequest handler. We need a draft work order handler. We need the work order class. Those would all be code files that we need to create.
Application Architecture State Machine Diagram
Now a work order is something that has a process that has a flow, and so we can use a state machine. If I have a work order right here, the first status I have is “draft”. The next status I have is “submitted”. And these are all facts, so these are all past tense as a status.
And then another status would be “assigned.” Maybe the next status is “in progress.” Then the next status is “complete.” You can see that we have transitions, a kind of asynchronous workflow, that this work order has.
So, if it is going from a draft to submit, then we are going to “submit” the work order. Others could be “assign”, “begin”, “complete”, etc.
Now say I have assigned it, but now want to bring it back; meaning, I want to bring it back to this status. You can go backward and say “unassign” or “unsubmit”. Or, if it is in progress I can “shelve” it; whatever word you want to use. If it is completed and I go in progress, maybe I “reopen” the work order.
How to Translate this into Actual Code
Now, what do we have here? We have a lot of lines and a lot of bubbles. Well, we already have our work order. It is an entity. We have declared it to be the root of an aggregate, in domain-driven design speak, and Draft is a status. Whether we use an enumeration or whether we use an enumerated class, Draft is essentially an instance of status, same with all these other statuses. And then every one of these lines connecting the bubbles is a command.
So, let us just make up an abstraction. We need an abstraction to talk about and reason out. So, let us just make one up – IStateTransition. And so, if we have an IStateTransition, that means that every one of these lines could represent an implementation of IStateTransition.
All right. So, what would we really need this abstraction to be defined as? We absolutely need to know if this “is valid”. We need the work order to see if it is valid. And then how about “is allowed”? Is Allowed includes the work order and the employee who is trying to do it.
That way we can create an implementation for the state transitions to check validity of the action from state to state and the authorization for the user to perform the transition.
So, this is how we make these types of pattern decisions. This is a state diagram. Let us just count how many of these implementation classes we are going to create? We need eight implementations of this class.
An Application Architecture Diagram is Worth a Thousand Words
As we draw these diagrams, it becomes really obvious what tasks to perform and how to fill up our development task board. This is how to break up a feature that has come on our Kanban board and decompose it to the different development work that we need to perform. Use a whiteboard, use paper, but do not immediately go to Visio. Just use your pen or pencil and draw some of these application architecture diagrams.
In order to get clarity with everybody, you need to have a picture because if you’re just using your words, everybody is trying to get the same picture in their head of what you intend to do and how to do it. Everybody needs to build that picture for themselves. If you let everyone do it just from words, everyone is going to have a slightly different picture.
You need to distill your understanding and your decisions into a picture. Now you’ve given them the picture instead of forcing them to come up with their own, which is invariably going to be a little bit different than the picture in your head.
So, I hope that helps. If you have an architecture question, just tweet me @jeffreypalermo, and I will be happy to answer your questions.
Originally published October 29, 2020. Information refreshed July 13, 2022.