Architecture: The CASA Model- Getting to the Core


About 9 years ago, I was looking at code from a client and dealing with how hard it was to maintain compared to new code I was writing. In the process, I started seeing patterns in the code that I saw in numerous code bases I had worked in over the years. In particular:

  1. Most code was not organized well for maintainability
  2. Best practices were known by all developers, but few were practiced
  3. Very little automated testing
  4. The systems were not domain focused
  5. Switching from one user interface (say windows forms application) to another (web) required a lot of work, as the developers created a new application.

One issue that led to this mass of spaghetti code was the n-tier architecture idea, which gave people the principle that an application is made up of a user interface, business logic and data. Thus, a windows application and a web application were two different things. Naturally, since they were different things, you had to build them separately. In reality web application and a windows applications, as we know them, are not applications. Web and windows are two means of presenting applications. 

That simple paradigm shift lead me to start considering methodologies used for development, so I created what I thought was a new, or at least varied, methodology. I called it Core as Application. The original premise looked like this.

 

Core As Application, circa 2010

In this model, the Core IS the Application. This means an application is made up of domain objects (State) and actions on the domain objects (Behavior). In short, software is a bunch of BS? 

To facilitate the application, I need a means of presenting information to an interacting with users (May be human users or machine users). And, I need a means of persisting state between sessions. I should be able to switch presentations and persistence mechanisms without writing a NEW application, as the Core IS the application. 

NOTE: Core AS Application was a progression of Core Is Application so I could have a nice acronym.

There were a couple of things I did not realize at the time.

  1. Core as Application is NOT a methodology. It is a model. 
  2. Other people were working the same model at the same time, although focused very much on the architecture. You know them as Onion Architecture, Hexagonal Architecture and Clean Architecture

On point #1, I refined the concept to the Core AS Application model (CASA for short, since it needed a cute name). On point #2, all of the “architectures” mentioned deal with the same concept: domain models are the center, followed by business logic. I can can cover these in another post in the future.

Before moving forward, let’s cover a few terms as I envision them right now in CASA. The definitions are oversimplifications to an extent, but help keep you, the reader, on track.

Capability: A business capability is a function or service a business provides to its clients, internal and/or external.  Applications should be centered around capabilities and systems around 

Application: The software solution providing centered around a business capability.  In CASA, the application will be a single core with contracts separating it from presentation and persistence mechanisms.

System: A program or set of programs to solve a business problem that can span multiple capabilities. System can be the same as an application, in simple business problems, but can also required integration with other applications. As an example, an application that connects to multiple services to accomplish its work is a system. In CASA, if you see more than one core, it is an integrated system.

Solution: Solution is used here to indicate a packaging of separate projects to create an application. In this regard, it is synonymous to a Visual Studio Solution. A project will be a single Visual Studio project. 

In this post, I want to cover CASA as it exists today and why it works well in development.

The CASA Model 2018

The general principle of CASA is the application is the business logic at the center of the model. This means I can switch presentation and persistence without changing the application. I sit this core (behavior) on top of domain models (state), as the domain is the foundation of the application. I place contracts on either side of the Core, as I want to facilitate testing, which sits on top (the Contracts serve more than just aiding tests – to this in a moment). And testing, primarily automated, sits at the top. The model looks like this today.

There are a couple of differences between this model and the original.

  1. The domain is no longer part of the core, but the foundation. 
  2. Contracts have been added between the core and the persistence mechanism (in general, this will be a database)
  3. Contracts have been added between the core and the presentation mechanism (normally a user interface, but could be a service of some other machine to machine mechanism).
  4. Tests have been added on top.
  5. Adapters have been placed next to the contracts. 

In general, each of the various boxes will be contained in separate projects in a solution (assuming .NET, but the concept can be used in other programming paradigms). Here is a sample solution showing how this is used. 

For a better understanding of the value of the model, let me tie it back to some other industry concepts you might be familiar with, some of which were included in the design of the CASA model: Domain Driven Design, Contract-Fist Development, SOLID Design Principles, DRY, YAGNI and KISS, although some of the concepts are implicit rather than explicit.

  • Domain Driven Design – I don’t use all of Eric Evan’s domain driven principles, as I am rather stringent on separating my BS (behavior and state). I have 2 common domain projects in every solution: one for models and another for exceptions and I do not include behavior in a domain object, unless it is related to the object. As an example, an address may have a ToString() method that delivers a concatenated address – this is behavior, but specific to the object.
  • Contract First Development – The concept of writing concepts prior to developing is not new. In C++, you create interfaces first, in the form of .h (header) files. Delphi, which may, in some ways, be considered the inspiration for C#, also had a contract first approach. With REST APIs, the contract is a bit more implicit and found in the URL. For CASA, the idea is write the contracts first to ensure you understand the seams in your application.
  • Test Driven Development – I am definitely one that believes writing tests first helps you ensure your code is focused on what your users desire. I use code coverage metrics with my teams, but it is just a measure not a goal. 100% code coverage with bad tests is as unacceptable as very low coverage. I should also note acceptability is the real goal, not writing tests. 
  • SOLID Design Principles – Organizing code into specific areas of concern cover the (S Single Responsibility), while contracts handle the I (Interface Segregation) and aid the D (Dependency Inversion). The O (Open/Closed Principle) and L (Liskov Substitution) are not directly addressed by CASA, although using interfaces will help you think twice about breaking contracts, especially when you end up with multiple classes utilizing the same interface.
  • DRY – Do Not Repeat Yourself. This is not inherent in the model, so you have to create a process to handle this, as well as governance. For the process, when you find repeat code in multiple classes (or worse, projects), you should start refactoring ASAP. A couple of normal methods to employ would be to move the common code into a base class (two classes on the same interface, for example). Or you move the code into a separate class and/or project and reference it. As for the governance, if you are not using code reviews as part of your development process, you should. 
  • YAGNI and KISS – Also not directly covered in CASA, but you find the separation of the code makes it much easier to solve problems through testing and focusing on one problem at a time. Once you grasp the concept, you will find it easier to keep things simple and avoid bloating your code. If you do try to think ahead, you will find the model fights against you. In the future, I plan on talking a bit about testing with CASA and will add the link here.

Expanding CASA

As I started examining the model, I started thinking about solutions I had seen in various Enterprises. The area CASA seemed to fail is when you added a services layer to the application. First, some developers had an issue with the paradigm shift involved with thinking of services as a presentation concept. A bigger issue is what happens when you add a tier of services, as shown below:

I adjusted this by moving services to its own folder in the solution. This helped with the paradigm shift. Recently, I have found this also works with mobile applications, as you can use the same persistence library for the mobile application and the server side application. 

I should note, the service holds one application and the user interface, which used the service, holds another (at least potentially). So I had two cores, which means another solution. From an organization standpoint, you have two applications. When I add a second service, it becomes more evident. If you focus on microservices, which are mini applications with a service front end (yes, a simplification), it is absolutely clear an application with a service, layer or not, is actually multiple applications. I am not one for having too many projects in a solution, but as long as you can develop without massive compile and test cycles, it is a place to start. Solve then refactor.

From the standpoint of the model, it became clear I simply needed to chain two cores together with a service, which looks like this:

Moving to Mobile

In the past 6 months, I have gotten heavily focused on mobile presentation in React and Xamarin. As React cannot be directly compiled using Visual Studio, I won’t cover it here (I actually think I might be able to solve that problem, but that is another story). Xamarin is a bit easier, but changes a few things in organization.

In particular, with Xamarin, you end up having to compile at least parts of your core for each platform, especially in an offline application. You will be persisting to both the service (server side) and the mobile database from the mobile device. And to a server side database when the mobile application contacts the server to persist.

  1. Create a subfolder called Mobile under the Present folder. This is where the Xamarin.Forms project and the iOS and Android specific libraries go (I will likely rework this in the future, as these are not technically presentation libraries, but utilities).
  2. Make sure any libraries that are used on the server and the mobile device are .NET standard. NOTE: I think Core can work, but standard provides a few other options. 
  3. If you require any functionality that uses the full .NET Framework, ensure it is behind a service, as Xamarin cannot compile the full framework to native code.
  4. To facilitate the mobile application saving locally and on the server, configure it to use 2 implementations of the same contract: one to save locally and one to save via the service. The service will use one implementation of the contrract, of course.

I currently have the service in the same solution as the mobile application. When I separate it out as a separate product, I will have two solutions pointing to the same contracts for persistence. This may necessitate placing the contracts as their own product to avoid development in one solution breaking the other. I think the team is small enough to not jump either of these hoops right now (NOTE: I envision an entry on product mindset this week and will link it here). 

So, here is the solution at present. (NOTE: This is a work in progress, as I am still developing this. Thus far it is working)

This seems awfully hard?

Any time you switch paradigms, it is hard. For years after .NET was released I saw developers coding standard VB in VB.NET projects, as they had not made the paradigm shift. Microsoft made it easy to avoid making the shift by adding training wheels to VB in the form of the library Microsoft.VisualBasic.Compatibility. During the early .NET years, I used to recommend switching to C# as the paradigm shift would become part of learning the new language syntax. 

Once you get the paradigm shift, however, you will notice it becomes very easy to find code when you discover a bug. Part of this is in the organization the model provides, but a good deal of it is in learning the concept of a domain (which is outside the scope of this post).

Does it Work?

I have yet to see a case it does not. But I am also not arrogant enough to state you must use CASA to get the benefits. Below are the design principles I am using. If you already adhere to all of these principles, you might think twice before adopting what I have written here. Some of these principles go beyond the concepts in CASA and will be covered on this blog later.

  • Domain Driven Design – Focus on the business problem you are solving. Domain Driven Design will aid you in this principle. Your state objects should be named in terms familiar to your business.
  • Contract First Development – Understand the seams in your application before coding anything around the seams. To simplify, as you deal with different technical concerns, consider it a seam. In CASA, there is a contract for persistence, one for the core and one for any dependency that can be swapped out, or capability. If you ever feel another team could write a separate implementation, you write a contract. Why first? Because, like Test Driven Development, you learn about your code before you code it.
  • Requirements – Write requirements for acceptance. This is a pre-step for testing, as understanding what is acceptable will make it much easier to write a full enough set of tests.
  • Test Early – I am not a 100% purist in test driven, but I am a stickler for writing a test first on any problem you are not 100% sure of (which is almost everything) and adding tests on top of those that you are. (NOTE: Over time you will find some things you were 100% sure of at first, you should have written the test first, as you were cocky in your confidence). One more point: Acceptability should be a goal for all testing, not just acceptance testing. In standard TDD style, this means writing more test methods. In behavior driven, it means acceptability in your specifications.
  • Coverage – Code for acceptability, not percent. Percent is a good metric to find areas that might be lacking, but never incent on code coverage, as humans are very good at being creative to get incentives. Good coverage includes the following: 1) covering business use cases completely to ensure user acceptance 2) covering unacceptable cases 3) covering the fringe. I will focus on this in another post (and link here).
  • Testing errors – If a bug is found, or a lack of acceptability is discovered, you immediately write a failing test. If you cannot write a test that covers the condition that FAILS, you do not understand the bug well enough to fix it, so you SHOULD NOT try to fix it. (NOTE: lack of acceptability is not the same thing as a bug. If a user discovers something does not work how he or she intended, it is not a bug, but a change in understanding of what is acceptable – same process, but don’t have your team take the blame when someone realizes their vision does not work as they would like).
  • Build the tests up from the unit – If you are focused on covering for acceptability, you will find the exact same tests you run as unit tests are often run as integration testing. With contracts, you will mock the contract for your unit test and use a concrete implemenation of the contract for integration tests. If you use behavior focused tests, you will find you are running the same tests, just changing dependencies (did that blow your mind? If not, you are already in the Zen garden). Because I see the persistence store (database generally) as the castle that protects the king (data is king for business), I write an integration test on this early on. I might write it first, especially on non-user interfacing persistence applications. But I sometimes unit test the presentation first to ensure I understand what the user intended and then go to the database. Knowing how the user will interact with the system and how I store (optimized data store, not just a CRUD mock of the business objects), I can mock the persistence layer and unit test all core business functionality. If what I just wrote sounds a bit confusing, I will write a blog entry about this soon.
  • Product Mindset – This is the most complex topic (and deserving of its own blog entry – link here in the future). A few things I recommend. If you find you are no longer actively developing on part of your code, consider making it into a product. If you started with CASA at the start, these will likely end up being utility classes that you want to reuse, but this technique can also serve well if you have not use CASA, or similar, in the past. The main point is once you find a capability, add a contract and move the code to its own project, then to its own solution, and finally put it in your own NuGet repository. (I guess I also need to talk capability thinking in the future? – link here in the future).
  • Agility – This really has nothing to do with CASA, at least not directly. But Agile gets you thinking in small bits of functionality at a time, which works very well with CASA.

In Summary

In this entry, I focused on the Core AS Application, or CASA, model. CASA is a model that focuses on a visual representation of code organization with a focus on best practices, like Domain Driven Design, Contract First Development and SOLID Design Principles.

In the future, I will break this down further, focusing on how this works with various concerns, such as Requirements, Testing, Product Focus and Agile Development.

Peace and Grace,
Greg

Twitter: @gbworld