Skip to main content

Backend Architecture

Architecture of the .NET Core backend API.

Technology Stack

  • .NET 6.0: Runtime and framework
  • ASP.NET Core: Web framework
  • Entity Framework Core: ORM
  • SignalR: WebSocket support
  • MediatR: Mediator pattern
  • AutoMapper: Object mapping
  • Kafka: Event streaming

Project Structure

The backend lives under Src/backend/. The main API and workers are sibling projects (separate .csproj):

Src/backend/
├── API/ # Web API (REST + SignalR)
│ ├── Controllers/ # API controllers (Authentication, Post, Comment, Chat, etc.)
│ ├── Middlewares/ # Error handling, license, rate limit, host validation
│ ├── Dtos/, Helpers/ # DTOs and Swagger helpers
│ └── Program.cs # Startup
├── Application/ # Business logic (CQRS, MediatR)
│ └── Modules/ # Feature modules (Authentication, Post, Comment, Story, etc.)
├── Domain/ # Domain models and entities
│ └── Modules/ # Aggregates and value objects
├── Infrastructure/ # EF Core, repositories, Kafka, external services
├── Shared/ # Shared contracts and utilities
├── DatabaseApplication/ # Migrations and seeders
├── SharedWorker/ # Base worker, Kafka consumer, JWT, SignalR client
├── GraphWorker/ # Social graph (Neo4j)
├── ScoringWorker/ # Content scoring, recommendation
├── TrendingWorker/ # Trending decay
├── HashtagWorker/ # Hashtag extraction/indexing
├── ElasticsearchSyncWorker/ # Sync content to Elasticsearch
├── BlogAutoGenerationWorker/ # AI blog generation
├── InteractionWorker/ # Engagement processing
├── MediaProcessingWorker/ # Media processing
├── WebSocketWorker/ # Real-time notifications
├── WebPushNotificationWorker/ # Push notifications
└── ChatWorker/ # Real-time chat (SignalR hub)

Architecture Pattern

Clean Architecture

┌─────────────────────────────────────┐
│ API Layer │
│ (Controllers, Middlewares) │
└──────────────┬──────────────────────┘

┌──────────────▼──────────────────────┐
│ Application Layer │
│ (Services, Use Cases, DTOs) │
└──────────────┬──────────────────────┘

┌──────────────▼──────────────────────┐
│ Domain Layer │
│ (Entities, Value Objects) │
└──────────────┬──────────────────────┘

┌──────────────▼──────────────────────┐
│ Infrastructure Layer │
│ (Database, External APIs) │
└─────────────────────────────────────┘

API Controllers

Controller Structure

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class PostsController : ControllerBase
{
private readonly IPostService _postService;

[HttpGet]
public async Task<ActionResult<PagedResult<PostDto>>> GetPosts([FromQuery] int page = 1)
{
var posts = await _postService.GetPostsAsync(page);
return Ok(posts);
}

[HttpPost]
public async Task<ActionResult<PostDto>> CreatePost([FromBody] CreatePostDto dto)
{
var post = await _postService.CreatePostAsync(dto);
return CreatedAtAction(nameof(GetPost), new { id = post.Id }, post);
}
}

Service Layer

Service Implementation

public class PostService : IPostService
{
private readonly IPostRepository _repository;
private readonly IEventPublisher _eventPublisher;

public async Task<PostDto> CreatePostAsync(CreatePostDto dto)
{
var post = new Post
{
Content = dto.Content,
AuthorId = dto.AuthorId,
CreatedAt = DateTime.UtcNow
};

await _repository.AddAsync(post);
await _eventPublisher.PublishAsync(new PostCreatedEvent(post));

return _mapper.Map<PostDto>(post);
}
}

Database Context

Entity Framework Setup

public class ApplicationDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }

protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}

Event-Driven Architecture

Event Publishing

public class PostCreatedEvent
{
public string PostId { get; set; }
public string AuthorId { get; set; }
public DateTime CreatedAt { get; set; }
}

// Publish event
await _eventPublisher.PublishAsync(new PostCreatedEvent
{
PostId = post.Id,
AuthorId = post.AuthorId,
CreatedAt = post.CreatedAt
});

Event Consumers (Workers)

Workers consume events from Kafka and process them asynchronously.

Authentication & Authorization

JWT Authentication

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = configuration["Jwt:Issuer"],
ValidAudience = configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"]))
};
});

Next Steps