Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Software Architecture with C# 12 and .NET 8

You're reading from   Software Architecture with C# 12 and .NET 8 Build enterprise applications using microservices, DevOps, EF Core, and design patterns for Azure

Arrow left icon
Product type Paperback
Published in Feb 2024
Publisher Packt
ISBN-13 9781805127659
Length 756 pages
Edition 4th Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Gabriel Baptista Gabriel Baptista
Author Profile Icon Gabriel Baptista
Gabriel Baptista
Francesco Abbruzzese Francesco Abbruzzese
Author Profile Icon Francesco Abbruzzese
Francesco Abbruzzese
Arrow right icon
View More author details
Toc

Table of Contents (26) Chapters Close

Preface 1. Understanding the Importance of Software Architecture FREE CHAPTER 2. Non-Functional Requirements 3. Managing Requirements 4. Best Practices in Coding C# 12 5. Implementing Code Reusability in C# 12 6. Design Patterns and .NET 8 Implementation 7. Understanding the Different Domains in Software Solutions 8. Understanding DevOps Principles and CI/CD 9. Testing Your Enterprise Application 10. Deciding on the Best Cloud-Based Solution 11. Applying a Microservice Architecture to Your Enterprise Application 12. Choosing Your Data Storage in the Cloud 13. Interacting with Data in C# – Entity Framework Core 14. Implementing Microservices with .NET 15. Applying Service-Oriented Architectures with .NET 16. Working with Serverless – Azure Functions 17. Presenting ASP.NET Core 18. Implementing Frontend Microservices with ASP.NET Core 19. Client Frameworks: Blazor 20. Kubernetes 21. Case Study 22. Case Study Extension: Developing .NET Microservices for Kubernetes 23. Answers
24. Other Books You May Enjoy
25. Index

Common cases where the requirements gathering process impacts system results

All the information discussed up to this point in the chapter is useful if you want to design software following the principles of good engineering. Rather than advocating for traditional or agile development methods, the emphasis is on building software in a professional manner.

It is also a good idea to know about some cases in which failing to perform the activities you read about can cause some trouble for a software project. The following cases intend to describe what can go wrong, and how the preceding techniques can help a development team to solve the associated problems.

In most cases, very simple actions can guarantee better communication between the team and the customer, and this easy communication flow can transform a big problem into a real solution. Let us examine three common cases where requirements gathering can impact software performance, functionality, and usability.

Case 1 – my website is too slow to open that page!

Performance is one of the biggest problems that you as a software architect will deal with during your career. The reason why this aspect of any software is so problematic is that we do not have infinite computational resources to solve problems. The cost of computation is still high, especially if you are talking about software with a high number of simultaneous users.

You cannot solve performance problems by writing requirements. However, you will not end up in trouble if you write them correctly. The idea here is that requirements must present the desired performance of a system. A simple sentence describing this can help the entire team that works on the project:

Non-functional requirement: Performance – any web page of this software shall respond in at least 2 seconds, even when 1,000 users are accessing it concurrently.

The preceding sentence just lets everybody (users, testers, developers, architects, managers, and so on) know that any web page has a target to achieve. This is a good start, but it is not enough. A great environment for developing and deploying your application is also important. This is where .NET 8 can help you a lot; especially if you are talking about web apps, ASP.NET Core is considered one of the fastest options to deliver solutions today.

When it comes to performance, you, as a software architect, should consider the use of the techniques listed in the following sections together with specific tests to guarantee this non-functional requirement. It is also important to mention that ASP.NET Core will help you to use them easily, together with some Platform as a Service (PaaS) solutions delivered by Microsoft Azure.

Understanding backend caching

Caching is a great technique to avoid time-consuming and redundant queries. For instance, if you are fetching car models from a database, the number of cars in the database can increase, but the models themselves will not change. Once you have an application that constantly accesses car models, a good practice is to cache that information.

It is important to understand that a cache is stored in the backend and that cache is shared by the whole application (in-memory caching). A point to focus on is that when you are working on a scalable solution, you can configure a distributed cache using the Azure platform. In fact, ASP.NET provides both in-memory caching and distributed caching, so you can decide on the one that best fits your needs. Chapter 2, Non-Functional Requirements, covers scalability aspects in the Azure platform.

It is also important to mention that caching can happen in the frontend, in proxies along the way to the server, CDNs, and so on.

Applying asynchronous programming

When you develop ASP.NET applications, you need to keep in mind that your app needs to be designed for simultaneous access by many users. Asynchronous programming lets you do this simply, by giving you the keywords async and await.

The basic concept behind these keywords is that async enables any method to run asynchronously. On the other hand, await lets you synchronize the call of an asynchronous method without blocking the thread that is calling it. This easy-to-develop pattern will make your application run without performance bottlenecks and bring better responsiveness. This book will cover more about this subject in Chapter 2, Non-Functional Requirements.

Dealing with object allocation

One very good tip to avoid poor performance is to understand how the Garbage Collector (GC) works. The GC is the engine that will free memory automatically when you finish using it. There are some very important aspects of this topic, due to the complexity that the GC has.

Some types of objects are not collected by the GC if you do not dispose of them. The list includes any object that interacts with I/O, such as files and streaming. If you do not correctly use the C# syntax to create and destroy this kind of object, you will have memory leaks, which will deteriorate your application’s performance.

The incorrect way of working with I/O objects is:

System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\sample.txt");
file.WriteLine("Just writing a simple line");

The correct way of working with I/O objects is:

using System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\sample.txt");
file.WriteLine("Just writing a simple line");

It might be worth noting that this correct approach also ensures the file gets written (it calls FileStream.Flush() to dispose of its resources gracefully). In the incorrect example, the contents might not even be written to the file. Even though the preceding practice is mandatory for I/O objects, it is totally recommended that you keep doing this in all disposable objects. Indeed, using code analyzers in your solutions with warnings as errors will prevent you from accidentally making these mistakes! This will help the GC and will keep your application running with the right amount of memory. Depending on the type of object, mistakes here can snowball, and you could end up with other bad things on a bigger scale, for instance, port/connection exhaustion.

Another important aspect that you need to know about is that the time spent by the GC collecting objects will interfere with the performance of your app. Because of this, avoid allocating large objects and be careful with event handling and week references; otherwise, it can lead to you always waiting for the GC to finish its task.

Getting better database access

One of the most common performance Achilles’ heels is database access. The reason why this is still a big problem is a lack of attention paid while writing queries or lambda expressions to get information from a database. This book will cover Entity Framework Core in Chapter 13, Interacting with Data in C# – Entity Framework Core, but it is important to know what to choose and the correct data information to read from a database. Filtering columns and rows is imperative for an application that wants to deliver on performance.

The good thing is that best practices related to caching, asynchronous programming, and object allocation fit completely into the environment of databases. It is only a matter of choosing the correct pattern to get better-performing software.

Case 2 – the user’s needs are not properly implemented

The more technology is used in a wide variety of areas, the more difficult it is to deliver exactly what the user needs. Maybe this sentence sounds weird to you, but you must understand that developers, in general, study how to develop software, but they rarely study delivering the needs of a specific area. Of course, it is not easy to learn how to develop software, but it is even more difficult to understand a specific need in a specific area. Software development nowadays delivers software to all types of industries. The question here is how can a developer, whether a software architect or not, evolve enough to deliver software in the area they are responsible for?

Gathering software requirements will help you in this tough task; writing them will make you understand and organize the architecture of the system. There are several ways to minimize the risks of implementing something different from what the user really needs:

  • Prototyping the interface to achieve an understanding of the user interface faster
  • Designing the data flow to detect gaps between the system and the user operations
  • Frequent meetings to stay up to date on the user’s current needs and be aligned with incremental deliveries

Again, as a software architect, you will have to define how the software will be implemented. Most of the time, you are not going to be the one who programs it, but you will always be the one responsible for this. For this reason, some techniques can be useful to avoid the wrong implementation:

  • Requirements are reviewed by the developers to guarantee that they understand what they need to develop.
  • Code inspection to validate a predefined code standard. We will cover this in Chapter 4, Best Practices in Coding C# 12.
  • Meetings to eliminate impediments.

Remember, making sure the implementation matches the user’s needs is your responsibility. Use every tool you can to do so.

Case 3 – the usability of the system does not meet the user’s needs

Usability is a key point for the success of a software project. The way the software is presented and how it solves a problem will determine whether the user wants to use it or not. As a software architect, you must keep in mind that delivering software with good usability is mandatory nowadays.

There are basic concepts of usability that this book does not intend to cover, but a good way to meet the user’s needs when it comes to usability is by understanding who is going to use the software. Design Thinking can help you a lot with that, as was discussed earlier in this chapter.

Understanding the user will help you to decide whether the software is going to run on a web page, a cell phone, or even in the background. This understanding is very important to a software architect because the elements of a system will be better presented if you correctly map who will use them.

On the other hand, if you do not care about that, you will just deliver software that works. This can be good for a short time, but it will not exactly meet the real needs that made a person ask you to architect the software. You must keep in mind the options and understand that good software is software designed to run on many platforms and devices.

You will be happy to know that .NET 8 is an incredible cross-platform option for that. So, you can develop solutions to run your apps in Linux, Windows, Android, and iOS. You can run your applications on big screens, tablets, cell phones, and even drones! You can embed apps on boards for automation or in HoloLens for mixed reality. Software architects must be open-minded to design exactly what their users need.

You have been reading a chapter from
Software Architecture with C# 12 and .NET 8 - Fourth Edition
Published in: Feb 2024
Publisher: Packt
ISBN-13: 9781805127659
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image