build modular monolith with .net with spa

In today's software development landscape, creating scalable and maintainable applications is crucial. One approach that has gained popularity is the modular monolith architecture, combined with Single Page Applications (SPAs). This article will guide you through the process to build modular monolith with .NET with SPA, offering a robust solution for modern web applications.

Table of Contents

  1. Introduction to Modular Monolith Architecture
  2. Benefits of Building a Modular Monolith with .NET
  3. Setting Up the Development Environment
  4. Designing the Modular Structure
  5. Implementing Core Modules
  6. Creating the SPA Frontend
  7. Integrating .NET Backend with SPA
  8. Testing and Quality Assurance
  9. Deployment Strategies
  10. Maintenance and Scalability
  11. Conclusion

<a name="introduction"></a>

1. Introduction to Modular Monolith Architecture

Before we dive into how to build modular monolith with .NET with SPA, let's understand what a modular monolith is. A modular monolith is an architectural pattern that combines the simplicity of a monolithic application with the modularity of microservices. It allows developers to create a single, deployable unit while maintaining clear boundaries between different functional areas of the application.

Key characteristics of a modular monolith include:

  • Clear separation of concerns
  • Loose coupling between modules
  • High cohesion within modules
  • Shared infrastructure and resources

When combined with a SPA frontend, this architecture provides a powerful foundation for building scalable and maintainable web applications.

<a name="benefits"></a>

2. Benefits of Building a Modular Monolith with .NET with SPA

There are several advantages to choosing this architectural approach:

  1. Simplified development: By using a single codebase, developers can avoid the complexities of distributed systems.
  2. Easier deployment: The entire application can be deployed as a single unit.
  3. Improved performance: Reduced network overhead compared to microservices.
  4. Flexibility: Modules can be extracted into separate services if needed in the future.
  5. Better organization: Clear boundaries between modules improve code organization and maintainability.
  6. Enhanced user experience: SPAs provide smooth, responsive interfaces.

<a name="setup"></a>

3. Setting Up the Development Environment

To build modular monolith with .NET with SPA, you'll need the following tools:

  • Visual Studio 2022 or later
  • .NET 6.0 SDK or later
  • Node.js and npm (for SPA development)
  • Git for version control

Follow these steps to set up your environment:

  1. Install Visual Studio 2022, selecting the "ASP.NET and web development" workload.
  2. Install the latest .NET SDK from the official Microsoft website.
  3. Install Node.js and npm from the official Node.js website.
  4. Install Git from the official Git website.

With these tools in place, you're ready to start building your modular monolith application.

<a name="design"></a>

4. Designing the Modular Structure

When you build modular monolith with .NET with SPA, it's crucial to design a clear and logical structure for your application. Here's a suggested structure:

MonolithSolution/
├── src/
│   ├── Core/
│   │   ├── ModularMonolith.Core/
│   ├── Modules/
│   │   ├── ModularMonolith.Module1/
│   │   ├── ModularMonolith.Module2/
│   │   ├── ModularMonolith.Module3/
│   ├── Infrastructure/
│   │   ├── ModularMonolith.Infrastructure/
│   ├── Web/
│   │   ├── ModularMonolith.Web/
│   ├── SPA/
│   │   ├── modular-monolith-spa/
├── tests/
│   ├── ModularMonolith.Core.Tests/
│   ├── ModularMonolith.Module1.Tests/
│   ├── ModularMonolith.Module2.Tests/
│   ├── ModularMonolith.Module3.Tests/
│   ├── ModularMonolith.Infrastructure.Tests/
│   ├── ModularMonolith.Web.Tests/

This structure separates concerns and allows for easy navigation and maintenance of your codebase.

<a name="core-modules"></a>

5. Implementing Core Modules

When you build modular monolith with .NET with SPA, implementing core modules is a crucial step. Start by creating the following projects:

  1. ModularMonolith.Core: Contains shared interfaces, base classes, and utility functions.
  2. ModularMonolith.Infrastructure: Implements cross-cutting concerns like logging, caching, and data access.
  3. ModularMonolith.Web: The main ASP.NET Core application that hosts the API endpoints.

For each functional module (e.g., Module1, Module2, Module3), create a separate project that contains:

  • Domain models
  • Business logic
  • Data access layer
  • API controllers

Here's an example of how to structure a module:

// ModularMonolith.Module1/Models/SomeEntity.cs
namespace ModularMonolith.Module1.Models
{
    public class SomeEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

// ModularMonolith.Module1/Services/ISomeService.cs
namespace ModularMonolith.Module1.Services
{
    public interface ISomeService
    {
        Task<SomeEntity> GetByIdAsync(int id);
        Task<IEnumerable<SomeEntity>> GetAllAsync();
        Task CreateAsync(SomeEntity entity);
    }
}

// ModularMonolith.Module1/Services/SomeService.cs
namespace ModularMonolith.Module1.Services
{
    public class SomeService : ISomeService
    {
        // Implementation
    }
}

// ModularMonolith.Module1/Controllers/SomeController.cs
namespace ModularMonolith.Module1.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class SomeController : ControllerBase
    {
        private readonly ISomeService _someService;

        public SomeController(ISomeService someService)
        {
            _someService = someService;
        }

        [HttpGet("{id}")]
        public async Task<ActionResult<SomeEntity>> Get(int id)
        {
            var entity = await _someService.GetByIdAsync(id);
            if (entity == null)
                return NotFound();
            return Ok(entity);
        }

        // Other actions...
    }
}

<a name="spa-frontend"></a>

6. Creating the SPA Frontend

To build modular monolith with .NET with SPA, you'll need to create a frontend application. Popular choices for SPAs include:

  • React
  • Angular
  • Vue.js

For this example, let's use React. Here's how to set up a new React application:

  1. Navigate to the SPA directory in your solution.
  2. Run the following command to create a new React app:
npx create-react-app modular-monolith-spa
  1. Set up the project structure:
modular-monolith-spa/
├── src/
│   ├── components/
│   ├── pages/
│   ├── services/
│   ├── App.js
│   ├── index.js
├── public/
├── package.json
  1. Install necessary dependencies:
npm install axios react-router-dom
  1. Create API service to communicate with your .NET backend:
javascriptCopy// src/services/api.js
import axios from 'axios';

const API_BASE_URL = 'https://your-api-url.com/api';

export const getSomeEntity = async (id) => {
  const response = await axios.get(`${API_BASE_URL}/some/${id}`);
  return response.data;
};

// Add other API calls...
  1. Create components and pages to display and interact with your data.

<a name="integration"></a>

7. Integrating .NET Backend with SPA

To build modular monolith with .NET with SPA successfully, you need to integrate your backend with the frontend. Follow these steps:

  1. Configure CORS in your .NET application:
csharpCopy// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowSPA", builder =>
    {
        builder.WithOrigins("http://localhost:3000")
               .AllowAnyHeader()
               .AllowAnyMethod();
    });
});

// ... other configurations

var app = builder.Build();

app.UseCors("AllowSPA");

// ... other middleware
  1. Set up proxy in your React application to avoid CORS issues during development:
jsonCopy// package.json
{
  "proxy": "https://localhost:5001"
}
  1. Update your API service to use relative URLs:
javascriptCopy// src/services/api.js
import axios from 'axios';

const API_BASE_URL = '/api';

export const getSomeEntity = async (id) => {
  const response = await axios.get(`${API_BASE_URL}/some/${id}`);
  return response.data;
};

// Add other API calls...

<a name="testing"></a>

8. Testing and Quality Assurance

When you build modular monolith with .NET with SPA, thorough testing is essential. Implement the following types of tests:

  1. Unit tests for individual components and services
  2. Integration tests for API endpoints
  3. End-to-end tests for the entire application

For .NET, use xUnit or NUnit for unit and integration tests. For the SPA, use Jest and React Testing Library for component and integration tests.

Example of a .NET unit test:

// ModularMonolith.Module1.Tests/Services/SomeServiceTests.cs
public class SomeServiceTests
{
    [Fact]
    public async Task GetByIdAsync_ReturnsEntity_WhenEntityExists()
    {
        // Arrange
        var mockRepo = new Mock<ISomeRepository>();
        var expectedEntity = new SomeEntity { Id = 1, Name = "Test" };
        mockRepo.Setup(repo => repo.GetByIdAsync(1)).ReturnsAsync(expectedEntity);
        var service = new SomeService(mockRepo.Object);

        // Act
        var result = await service.GetByIdAsync(1);

        // Assert
        Assert.Equal(expectedEntity, result);
    }
}

<a name="deployment"></a>

9. Deployment Strategies

To deploy your modular monolith with .NET and SPA, consider the following strategies:

  1. Single-server deployment: Deploy both the .NET backend and SPA frontend on the same server.
  2. Separate deployments: Deploy the .NET backend on one server and the SPA on another (e.g., using a CDN).
  3. Containerization: Use Docker to containerize your application for easier deployment and scaling.

Example Dockerfile for the .NET backend:

dockerfileCopyFROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["ModularMonolith.Web/ModularMonolith.Web.csproj", "ModularMonolith.Web/"]
RUN dotnet restore "ModularMonolith.Web/ModularMonolith.Web.csproj"
COPY . .
WORKDIR "/src/ModularMonolith.Web"
RUN dotnet build "ModularMonolith.Web.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ModularMonolith.Web.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ModularMonolith.Web.dll"]

<a name="maintenance"></a>

10. Maintenance and Scalability

To ensure long-term success when you build modular monolith with .NET with SPA, focus on:

  1. Continuous Integration and Deployment (CI/CD): Implement automated build, test, and deployment pipelines.
  2. Monitoring and Logging: Use tools like Application Insights or Serilog for comprehensive logging and monitoring.
  3. Performance Optimization: Regularly profile and optimize both the backend and frontend code.
  4. Security Updates: Keep all dependencies up-to-date and perform regular security audits.
  5. Scalability Planning: Design your application to allow for future growth, potentially extracting modules into separate services if needed.

<a name="conclusion"></a>

11. Conclusion

Building a modular monolith with .NET and SPA offers a powerful and flexible approach to modern web application development. By following the steps outlined in this guide, you can create a well-structured, maintainable, and scalable application that combines the best of both monolithic and modular architectures.

Remember that the key to success when you build modular monolith with .NET with SPA is to maintain clear boundaries between modules, ensure loose coupling, and continuously refactor and improve your codebase. With proper planning and execution, your modular monolith can serve as a solid foundation for your application's growth and evolution.

By embracing this architecture, you can enjoy the benefits of simplified development and deployment while maintaining the flexibility to adapt to changing requirements. Whether you're building a small business application or a large-scale enterprise system, the modular monolith approach with .NET and SPA can help you achieve your goals efficiently and effectively.

Sign in to leave a comment