This post is part of a nine post series about the SOLID principles and code architecture. You can check all the posts here:
- Software Architecture in Game Development
- The Single Responsibility Principle
- The Open Closed Principle
- The Liskov Substitution Principle
- Conceptual Meaning Of Interfaces
- The Interface Segregation Principle
- The Dependency Inversion Principle
- SOLID: How to use it, Why and When
- SOLID For Unity Monobehaviours
What is Software Architecture
Software architecture is about creating dependencies in our code, in a way that our program is easy to change. In that regard, because all programs we write have dependencies between their classes, all programs have an architecture anyway, so any rules about software architecture are about writing the dependencies of the modules of our code in a way that reduce the rigidity, fragility, immobility and viscosity of our program.
Our code compiles to a program that is going to be used by other people, so it is only natural that our architecture is based on the needs of the users of our program. The two most used ways to create our architecture properly, are based on the user stories and the use cases.
Those user stories and use cases, give birth to the business rules which are the core of our program. The business rules, separate the core logic of our program from the delivery mechanism and the tools that we use to implement this delivery mechanism.
So in the end, architecture is not only about the dependencies between our classes, but also how groups of those classes are created to represent the business rules, how those business rules depend on each other and how those business rules as a whole are completely separated from the rest of our code that is a delivery mechanism to the user, like the UI and the tools that help us represent the data we need, for example our database.
What are Use Cases and User Stories
User stories and use cases, are different ways that represent the needs of whoever is using our program. I won’t go into much detail here, but in general, user stories are a more abstract way of how a user interacts with the different parts of our code to achieve a result and use cases are more about the concrete steps that a user takes while is using our program.
The important thing is that both user stories and use cases don’t contain anything about the delivery mechanism. UI can’t be used to describe those cases. User stories and use cases cannot be described with words like button, or pop-up, but are described with words that reveal the needs of the user, like moving his character in the game world (and not pressing a button or moving the mouse) and talking to NPC’s (not playing audio or displaying text)
How those needs are represented and delivered to the user, is not something that belongs to the business rules and for this reason a good architecture takes care to separate the layers between the core logic of our code which are our business rules and any delivery mechanism which is the UI and any other tool that can help us deliver the data we need to our core logic. This separation is often achieved, by introducing layers between those two.
Why It Is Useful
The core of our code that is composed by our business rules, describes the main usages of our program and as such it’s not only important to be separated from the rest of our code, but also to not have any dependencies to it.
The dependencies of the layers of our program, should be constructed in a way that the layers that are more likely to change are dependent on the layers that are less likely to change and not the other way around.
Achieving that, lowers the rigidity, fragility, immobility and viscosity of our program.
A rigid program is a program that has a high coupling between its classes and a lot of dependencies between its modules that describe a system of the program. A highly rigid program makes any changes to a class hard to implement because it will create the need to make changes to many other classes too.
A fragile program, is a program that any change to a class or a system will change the behavior of other classes or systems that are dependent on those. This will create bugs in a seemingly unrelated part of our program to the one we made the change.
An immobile program, is characterized by the difficulty to extract certain systems and internal components from the code, so that they can be reused by other parts of the program that may need this functionality.
Finally, viscosity, represents the problem that can be created when we need a new feature, but that feature has to be added across multiple layers of our code, instead of one place.
Benefits of Good Architecture
A good architecture, provides several benefits to our program. The most obvious one, is that makes our program easy to change. Any time that we may need a change, either that change is a change in an existing system, or is the addition of a new feature, can be done faster, because our dependencies are created in a way that other unrelated parts of our code aren’t influenced from this change.
Another benefit of good architecture, is that it allows us to postpone decisions about the tools and frameworks that we need to use. By creating our business layer we can delay any decisions, that involve the delivery mechanisms, to the future. This is not only true for our UI, but also for any mechanism that is not part of the core logic of our program, for example the save system.
A final benefit of good architecture, is that by separating the systems of our program, we can change the tools that we use, if we find the need to do so. For example, we may find the need to use a different framework for the UI from the one that we had previously decided to use, or the need to use a different database.
Benefits of Good Architecture in Game Development
In game development, good architecture may seem unnecessary. After all, when a game is done, it probably won’t need many changes. We may create DLCs and additional content, but the main game stays relatively the same, unless we are talking about a GAAS game.
This is a wrong assumption though. A game is dependent to many other frameworks and tools. By having a good architecture, we can create a considerable part of our game, without the need to depend on already made tools for a long time. By deferring the decision of the code assets that we need, we make our job easier, because a tool is maintained by its creator and any bugs or missing features are going to affect our production rate. Every time that we add a new tool or framework in our game, it is not only our code that is dependent on the tool’s code, but ourselves are also dependent from that point on, to the developers of that tool.
A good architecture also, allows us to exchange the frameworks or tools for others that we may eventually decide that are better for us. After all, it is not only about the tools and frameworks that our game uses, but also about the game engine we have decided to use. If for any reason, we find ourselves in need to switch the game engine, a good architecture can be the difference between a couple of weeks of work and a couple of years.
A hidden benefit of good architecture, is that it allows us to evaluate the cost in money and time between the core logic of our game and the usage of any external tools and frameworks. By having a separation of the core logic and the tools, we can have a clear understanding of how much money and time each costs and make decisions accordingly.
Architecture is about Time Investment
Finally, we may mistakenly think, that because after release our code will probably won’t change, good architecture is not that important.
In reality though, a game programmer is there to create a program that benefits the one that is providing the requirements of the game. In game development our "client" is the game designer
, not the final users of our game.
Good architecture is not only about making changes to the code far into the feature, but also about being able to make more changes in a specific time span. Game development is a game of iterations and good architecture gives the opportunity to the game designers to try more things and make more changes to the gameplay in the same amount of time.
A good game designer with programmers that can make changes fast, will eventually create a better game, than an excellent game designer with programmers that have created a bad architecture. In the second case, the cost of each change will increase exponentially because of the technical debt and this will limit the amount of iterations the game designer can do until he finds the “fun” in the game.
Game development is different from the production of other programs, in a way that the requirements are never clear. The game designer can try different things until the mechanics of the game represent a feeling that the game will give to the players, because creating a game is an art. In contrast with traditional applications, where the requirements are usually more clear, because they are about providing a certain functionality to the end user.
Conclusion
Time is money and money is time. People usually think about investing time or money so that they can have more money in the future, but rarely think about investing time so that they can have more time in the future.
If code architecture could be described in one sentence, I would say that Architecture is about investing time
.
As with all investments, when someone starts, will usually have more losses than wins. Eventually though, time and experience will make those investments more and more worthwhile.
There are many principles that exist, which try to describe how to have a good architecture in our code at the lower level, which is the dependencies between our classes. KISS, YAGNI, DRY and SOLID to name a few. Although I find KISS, YAGNI and DRY useful, I also think that they are pretty abstract.
In contrast, I find SOLID really well-defined. There are many misconceptions about SOLID. For example a common misconception is that the Single Responsibility Principle is about each class doing only one thing. I will try to clear those misconceptions by describing each of the five SOLID principles and how they can be used in development in general and in game development more specifically, in detail in the next blog posts.
Thank you for reading and as always, if you have any questions or comments you can use the comments section or contact me directly via the contact form or by email. Also if you don’t want to miss any of the new blog posts, you can always subscribe to my newsletter or the RSS feed.