The Pragmatic REST APIs in ASP.NET Core course will teach you how to build production-ready REST APIs using the latest ASP.NET Core features and best practices.| www.milanjovanovic.tech
How often do you think about concurrency conflicts when writing code? You write the code for a new feature, confirm that it works, and call it a day. But one week later, you find out you introduced a nasty bug because you didn't think about concurrency. The most common issue is race conditions with two competing threads executing the same function. If you don't consider this during development, you introduce the risk of leaving the system in a corrupted state. In this week's newsletter, I'll ...| www.milanjovanovic.tech
In this week's newsletter, we'll see how we can work with locking in .NET 6. We won't talk about how the lock is actually implemented at the operating system level. We will focus on application-level locking mechanisms instead. Locking allows us to control how many threads can access some piece of code. Why would you want to do this?| www.milanjovanovic.tech
You start building a beautiful monolith system. Maybe a modular monolith. The system grows, and requirements are ever-changing. Slowly, cracks begin to appear in the system. This could be for organizational reasons and distributing the work across a team. Or it could be because of scaling issues and performance bottlenecks. You begin the process of evaluating the benefits and tradeoffs of possible solutions. At last, you come to a decision. It's time to migrate parts of the system to individu...| www.milanjovanovic.tech
Software architecture is a blueprint for how you should structure your system. You can follow this blueprint strictly, or you can give yourself varying levels of freedom. But when deadlines are tight, and you start cutting corners, that beautiful software architecture you built crumbles like a house of cards. How can you enforce your software architecture? By writing architecture tests. Architecture tests are automated tests that verify the structure and design of your code. You can use them ...| www.milanjovanovic.tech
.NET Aspire revolutionizes distributed application development by simplifying service discovery through configuration-based approaches that eliminate the complexity of service-to-service communication.| www.milanjovanovic.tech
Build a robust, automated CI/CD pipeline for .NET 9 applications using GitHub Actions and Azure App Service to transform deployments from stressful events into reliable, repeatable processes.| www.milanjovanovic.tech
A well-designed monolith can scale remarkably well, despite industry trends pushing toward microservices. From database sharding to message queues, learn practical strategies to grow your monolithic system effectively.| www.milanjovanovic.tech
Every article about modular monoliths tells you to use public APIs between modules, but they rarely explain why these APIs exist or how to design them properly. After building several large-scale modular monoliths, I've learned that public APIs are not just about clean code - they're about controlling chaos.| www.milanjovanovic.tech
The Modular Monolith Architecture course will teach you how to build production-ready applications using Modular Monolith Architecture, Domain-Driven Design, and CQRS.| www.milanjovanovic.tech
Long-running business processes often require multiple services working together, but traditional distributed transactions can be problematic at scale. Learn how to implement the Saga pattern using MassTransit to break complex workflows into manageable steps with proper error handling and state management.| www.milanjovanovic.tech
Not every API request needs to finish right away. Learn how to build better APIs by moving long-running tasks to the background. This guide shows practical examples using image processing in ASP.NET Core 9.| www.milanjovanovic.tech
Learn how to implement idempotency in ASP.NET Core Web APIs to improve reliability and prevent duplicate operations in distributed systems.| www.milanjovanovic.tech
Transitioning from a modular monolith to microservices can significantly enhance your system's scalability and your team's productivity, but it's a journey that requires careful planning and execution. In this practical guide, I'll walk you through the key steps of this migration process, from preparing your monolith and selecting the right module to extract, to implementing effective inter-service communication and data migration strategies, all based on real-world experience with .NET appli...| www.milanjovanovic.tech
.NET Aspire promises to revolutionize cloud-native development for .NET applications, but does it live up to the hype? In this hands-on review, I'll share my experience migrating a real project to Aspire, exploring its strengths, limitations, and potential impact on how we build distributed systems.| www.milanjovanovic.tech
If you were to glance at the folder structure of your system, could you tell what the system is about? Your architecture should communicate what problems it solves. This approach is called sreaming architecture.| www.milanjovanovic.tech
Redis is a popular choice for caching data, but its capabilities go far beyond that. One of its lesser-known features is Pub/Sub support. Redis channels offer an interesting approach for implementing real-time messaging in your .NET applications.| www.milanjovanovic.tech
Service discovery is a pattern that allows developers to use logical names to refer to external services, instead of physical IP addresses and ports. In this week's issue, we'll see how to implement service discovery in your .NET microservices with Consul.| www.milanjovanovic.tech
Caching is one of the simplest techniques to significantly improve your application's performance. In this newsletter, we will explore how to implement caching in ASP.NET Core applications.| www.milanjovanovic.tech
By designing your applications with resilience in mind, you can create robust and reliable systems, even when the going gets tough. In this newsletter, we'll explore the tools and techniques we have in .NET to build resilient systems.| www.milanjovanovic.tech
Distributed systems offer flexibility but introduce complexity, making troubleshooting a headache. Understanding how requests flow through your system is crucial for debugging and performance optimization. OpenTelemetry is an open-source observability framework that makes this possible.| www.milanjovanovic.tech
Modular monoliths blend the simplicity and robustness of traditional monolithic applications with the flexibility and scalability of microservices. Today, I'll introduce you to the modular monolith architecture and why you should know about it.| www.milanjovanovic.tech
Suppose you're building a modular monolith, a type of software architecture where different components are organized into loosely coupled modules. Or you might need to process data asynchronously. You'll need a tool or service that allows you to implement this.| www.milanjovanovic.tech
Cross-cutting concerns are software aspects that affect the entire application. These are your common application-level functionalities that span several layers and tiers. Cross-cutting concerns should be centralized in one location. This prevents code duplication and tight coupling between components. In today's newsletter, I'll show you how to integrate cross-cutting concerns in Clean Architecture.| www.milanjovanovic.tech
Modular monoliths are an architectural approach that's becoming very popular. They combine the benefits of modularity and monolithic design. Data isolation ensures that modules are independent and loosely coupled. Today, I will show you four data isolation approaches for modular monoliths| www.milanjovanovic.tech
Exceptions are for exceptional situations. But they will inevitably happen in your applications, and you need to handle them. You can implement a global exception handling mechanism or handle only specific exceptions. ASP.NET Core gives you a few options on how to implement this. So which one should you choose? Today, I want to show you an old and new way to handle exceptions in ASP.NET Core 8.| www.milanjovanovic.tech
Layered architectures are the foundation of many software systems. However, layered architectures organize the system around technical layers. And the cohesion between layers is low. What if you wanted to organize the system around features instead? This is where Vertical Slice Architecture comes in.| www.milanjovanovic.tech
How should you handle errors in your code? This has been a topic of many discussions, and I want to share my opinion. One school of thought suggests using exceptions for flow control. This is not a good approach because it makes the code harder to reason about. The caller must know the implementation details and which exceptions to handle. Exceptions are for exceptional situations. Today, I want to show you how to implement error handling using the Result pattern. It's a functional approach t...| www.milanjovanovic.tech
Today I want to show you how to use the CQRS pattern to build fast and scalable applications. The CQRS pattern separates the writes and reads in the application. This separation can be logical or physical and has many benefits. I'm also going to show you how to implement CQRS in your application using MediatR.| www.milanjovanovic.tech
Modular monoliths are becoming more popular in the software engineering community. The allure of Microservices is becoming less compelling. We also have seasoned veterans of our industry saying you should reconsider: > You shouldn't start a new project with microservices, even if you're sure your application will be big enough to make it worthwhile. — Martin Fowler Modular monoliths give you the logical architecture of Microservices without the operational complexity. You can safely determi...| www.milanjovanovic.tech
Large Microservice-based systems can consist of tens or even hunders of individual services. A client application needs to have all of this information to be able to make requests to the relevant microservice directly. However, this has numerous issues such as security concerns, complexity, and coupling. We can solve this by introducing an API gateway that acts as a reverse proxy to accept API calls from the client application, forwarding this traffic to the appropriate service. The API gatew...| www.milanjovanovic.tech
Today's modern applications must deliver the latest information without refreshing the user interface. If you need to introduce real-time functionality to your application in .NET, there's one library you will most likely reach for - SignalR. SignalR allows you to push content from your server-side code to any connected clients as changes happen in real-time. Here's what I'll teach you in this week's newsletter: - Creating your first SignalR Hub - Testing SignalR from Postman - Creating stron...| www.milanjovanovic.tech
If you're building a scalable application, it's a common requirement to offload some work in your application to a background job. A few examples of that are: - Publishing email notifications - Generating reports - Updating a cache - Image processing How can you create a recurring background job in .NET? Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems. There are three concepts you need to understand in Quar...| www.milanjovanovic.tech
MediatR is a popular library with a simple mediator pattern implementation in .NET. Here's a definiton taken from MediatR's GitHub: "In-process messaging with no dependencies." With the rise in popularity of the CQRS pattern, MediatR became the go-to library to implement commands and queries. However, MediatR also has support for the publish-subscribe pattern using notifications. You can publish an INotification instance and have multiple subscribers handle the published message. Until recent...| www.milanjovanovic.tech
Entity Framework Core (EF Core) is a popular ORM in .NET that allows you to work with SQL databases. EF Core uses a DbContext, which represents a session with the database and is responsible for tracking changes, performing database operations, and managing database connections. It's common to have only one DbContext for the entire application. But what if you need to have multiple DbContexts? In this week's newsletter we're going to explore: - When you may want to use multiple DbContexts - H...| www.milanjovanovic.tech
.NET • ASP.NET Core • Software Architecture| www.milanjovanovic.tech
In this newsletter, we'll break down the essentials of EF Migrations. We'll explore creating migrations, SQL scripts, applying migrations, migration tooling, and more.| www.milanjovanovic.tech
MassTransit is an open-source distributed application framework for .NET. It provides a messaging abstraction on top of the supported message transports. In this week's issue, I'll show you how to use MassTransit.| www.milanjovanovic.tech
EF Core is my favorite ORM for .NET applications. Yet, its many fantastic features sometimes go unnoticed. For example, query splitting, query filters, and interceptors. EF interceptors are interesting because you can do powerful things with them. For example, you can hook into materialization, handle optimistic concurrency errors, or add query hints. The most practical use case is adding behavior when saving changes to the database.| www.milanjovanovic.tech
More than 63%+ of organizations said in a Dzone survey that they are adopting Microservices for some or all of their applications. As more businesses adopt the use of Microservice architectures, we as developers have to become more skilled with Microservices communication. Working with distributed systems is both fun and challenging at the same time. One of those challenges is designing effective communication between services. More centralization or less centralization? More coupling or less...| www.milanjovanovic.tech
In software engineering, "coupling" means how much different parts of a software system depend on each other. If they are tightly coupled, changes to one part can affect many others. But if they are loosely coupled, changes to one part won't cause big problems in the rest of the system. Domain events are a Domain-Driven Design (DDD) tactical pattern that we can use to build loosely coupled systems. You can raise a domain event from the domain, which represents a fact that has occurred. And ot...| www.milanjovanovic.tech
What happens when a message is retried in an event-driven system? It happens more often than you think. The worst case scenario is that the message is processed twice, and the side effects can also be applied more than once. Do you want your bank account to be double charged? I'll assume the answer is no, of course. You can use the Idempotent Consumer pattern to solve this problem. In this week's issue I'll show you: - How the Idempotent Consumer pattern works - How to implement an Idempotent...| www.milanjovanovic.tech
Designing long-lived processes in a distributed environment is an interesting engineering challenge. And a well known pattern for solving this problem is a Saga. A Saga is a sequence of local transactions, where each local transaction updates the Saga state and publishes a message triggering the next step in the Saga. Sagas come in two forms: Orchestrated Choreographed With an orchestrated Saga, there's a central component responsible for orchestrating the individual steps. In a choreographed...| www.milanjovanovic.tech
Working with Microservices, or any distributed system for that matter, is difficult. In a distributed system many things can go wrong, and there are even research papers about this. If you want to explore this topic futher, I suggest that you read about the fallacies of distributed computing. Reducing the surface area for things to go wrong should be one of your goals, as an engineer. In this week's newsletter, we'll try to achieve exactly that using the Outbox pattern. How can you implement ...| www.milanjovanovic.tech
If you're working in a distributed system, you need to be able to communicate between multiple services. There are a few ways that you can implement this. Depending on your chosen approach, you can either introduce tight coupling between your services or stay loosely coupled. Loose coupling is an important quality in a distributed system. It will allow you to evolve your services independently. So how do you implement loosely coupled communication between services? You'll need a messaging sys...| www.milanjovanovic.tech
Every software engineer working with SQL databases needs to know about transactions. And since most of the time the SQL database will be abstracted by an ORM like EF Core, it's important to understand how you can work with transactions using the available abstractions. So today, I'll show you how to work with transactions in EF Core. Here's what we will cover: - Default transaction behavior - Creating transactions - Using existing transactions| www.milanjovanovic.tech
In this week's newsletter, we're going to explore the new ExecuteUpdate and ExecuteDelete methods that were released with EF7. ExecuteUpdate allows us to write a query and run a bulk update operation on the entities matching that query. Similarly, ExecuteDelete allows us to write a query and delete the entities matching that query. We can significantly improve performance using the new methods in some scenarios, and I'm going to show you what those scenarios are.| www.milanjovanovic.tech
In this week's newsletter, I'll show you how you can remove repetitive conditions in EF Core database queries. An example would be when you implement soft-delete, and have to check if a record was soft-deleted or not in every query. Also, it is practical if you're working in a multi-tenant system and need to specify a tenantId on every query. EF Core has a powerful feature called Query Filters that can help you remove repetitive conditions from your code.| www.milanjovanovic.tech
I recently ran into an issue with Entity Framework Core. The query I was running was constantly timing out. So I used a new EF Core feature called Query Splitting to significantly improve my performance.| www.milanjovanovic.tech