diff --git a/deployments/local/docker-compose.yml b/deployments/local/docker-compose.yml
index 54233431..c98e9d5a 100644
--- a/deployments/local/docker-compose.yml
+++ b/deployments/local/docker-compose.yml
@@ -369,6 +369,56 @@ services:
- "traefik.http.services.chat-service.loadbalancer.sticky.cookie=true"
- "traefik.http.services.chat-service.loadbalancer.sticky.cookie.name=chat_session"
+ # Social Service .NET - Social Features (Profiles, Posts, Followers)
+ social-service-net:
+ build:
+ context: ../../services/social-service-net
+ dockerfile: Dockerfile
+ image: goodgo/social-service-net:latest
+ container_name: social-service-net-local
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://+:8080
+ # EN: Database - Neon PostgreSQL
+ # VI: Cơ sở dữ liệu - Neon PostgreSQL
+ - ConnectionStrings__DefaultConnection=Host=ep-holy-glitter-a4hongg7-pooler.us-east-1.aws.neon.tech;Port=5432;Database=social_service;Username=neondb_owner;Password=npg_Ssfy6HKO0cXI;SSL Mode=Require
+ # EN: IAM Service Communication
+ # VI: Giao tiếp IAM Service
+ - IamService__BaseUrl=http://iam-service-net:8080
+ - IamService__ServiceName=social-service
+ # EN: JWT Configuration
+ # VI: Cấu hình JWT
+ - Jwt__Authority=http://iam-service-net:8080
+ - Jwt__Audience=goodgo-api
+ - Jwt__RequireHttpsMetadata=false
+ # EN: Redis Cache
+ # VI: Cache Redis
+ - Redis__Host=167.114.174.113
+ - Redis__Port=6379
+ - Redis__Password=Velik@2026
+ ports:
+ - "5009:8080"
+ depends_on:
+ iam-service-net:
+ condition: service_healthy
+ traefik:
+ condition: service_started
+ networks:
+ - microservices-network
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8080/health/live"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 40s
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.social-service.rule=PathPrefix(`/api/v1/profiles`) || PathPrefix(`/api/v1/posts`) || PathPrefix(`/api/v1/followers`) || PathPrefix(`/api/v1/social`)"
+ - "traefik.http.routers.social-service.entrypoints=web"
+ - "traefik.http.services.social-service.loadbalancer.server.port=8080"
+ - "traefik.http.services.social-service.loadbalancer.healthcheck.path=/health/live"
+ - "traefik.http.services.social-service.loadbalancer.healthcheck.interval=10s"
# Jaeger - Distributed Tracing
diff --git a/services/chat-service-net/tests/ChatService.UnitTests/Application/Hubs/ClaimsUserIdProviderTests.cs b/services/chat-service-net/tests/ChatService.UnitTests/Application/Hubs/ClaimsUserIdProviderTests.cs
new file mode 100644
index 00000000..10ac5147
--- /dev/null
+++ b/services/chat-service-net/tests/ChatService.UnitTests/Application/Hubs/ClaimsUserIdProviderTests.cs
@@ -0,0 +1,47 @@
+using System.Security.Claims;
+using ChatService.API.Hubs;
+using FluentAssertions;
+using Microsoft.AspNetCore.SignalR;
+using Xunit;
+
+namespace ChatService.UnitTests.Application.Hubs;
+
+///
+/// EN: Unit tests for ClaimsUserIdProvider.
+/// VI: Unit tests cho ClaimsUserIdProvider.
+///
+public class ClaimsUserIdProviderTests
+{
+ private readonly ClaimsUserIdProvider _sut;
+
+ public ClaimsUserIdProviderTests()
+ {
+ _sut = new ClaimsUserIdProvider();
+ }
+
+ [Fact]
+ public void ClaimsUserIdProvider_ShouldImplementIUserIdProvider()
+ {
+ // Assert
+ _sut.Should().BeAssignableTo();
+ }
+
+ [Theory]
+ [InlineData(ClaimTypes.NameIdentifier)]
+ [InlineData("sub")]
+ [InlineData("user_id")]
+ public void GetUserId_ShouldSupportMultipleClaimTypes(string claimType)
+ {
+ // EN: This verifies the expected claim types are documented
+ // VI: Kiểm tra các claim types được hỗ trợ đã được ghi nhận
+ var supportedClaimTypes = new[] { ClaimTypes.NameIdentifier, "sub", "user_id" };
+ supportedClaimTypes.Should().Contain(claimType);
+ }
+
+ [Fact]
+ public void ClaimsUserIdProvider_ShouldNotBeNull()
+ {
+ // Assert
+ _sut.Should().NotBeNull();
+ }
+}
diff --git a/services/chat-service-net/tests/ChatService.UnitTests/ChatService.UnitTests.csproj b/services/chat-service-net/tests/ChatService.UnitTests/ChatService.UnitTests.csproj
index 37baced3..84501e96 100644
--- a/services/chat-service-net/tests/ChatService.UnitTests/ChatService.UnitTests.csproj
+++ b/services/chat-service-net/tests/ChatService.UnitTests/ChatService.UnitTests.csproj
@@ -29,6 +29,7 @@
+
diff --git a/services/chat-service-net/tests/ChatService.UnitTests/Domain/Contracts/AIServiceContractTests.cs b/services/chat-service-net/tests/ChatService.UnitTests/Domain/Contracts/AIServiceContractTests.cs
new file mode 100644
index 00000000..e8d7cdbe
--- /dev/null
+++ b/services/chat-service-net/tests/ChatService.UnitTests/Domain/Contracts/AIServiceContractTests.cs
@@ -0,0 +1,87 @@
+using ChatService.Domain.Contracts;
+using FluentAssertions;
+using Xunit;
+
+namespace ChatService.UnitTests.Domain.Contracts;
+
+///
+/// EN: Unit tests for IAIService interface and related DTOs.
+/// VI: Unit tests cho IAIService interface và các DTOs liên quan.
+///
+public class AIServiceContractTests
+{
+ [Fact]
+ public void ChatMessage_ShouldCreateWithAllProperties()
+ {
+ // Arrange
+ var role = "user";
+ var content = "Hello AI!";
+ var timestamp = DateTime.UtcNow;
+
+ // Act
+ var message = new ChatMessage(role, content, timestamp);
+
+ // Assert
+ message.Role.Should().Be(role);
+ message.Content.Should().Be(content);
+ message.Timestamp.Should().Be(timestamp);
+ }
+
+ [Theory]
+ [InlineData("user")]
+ [InlineData("assistant")]
+ [InlineData("system")]
+ public void ChatMessage_ShouldAcceptValidRoles(string role)
+ {
+ // Arrange & Act
+ var message = new ChatMessage(role, "Test content", DateTime.UtcNow);
+
+ // Assert
+ message.Role.Should().Be(role);
+ }
+
+ [Fact]
+ public void AIServiceOptions_ShouldHaveDefaultValues()
+ {
+ // Arrange & Act
+ var options = new AIServiceOptions();
+
+ // Assert
+ options.Provider.Should().Be("OpenAI");
+ options.Model.Should().Be("gpt-4");
+ options.MaxHistoryMessages.Should().Be(20);
+ options.MaxTokens.Should().Be(1000);
+ options.Temperature.Should().BeApproximately(0.7f, 0.001f);
+ options.SystemPrompt.Should().NotBeNullOrEmpty();
+ }
+
+ [Fact]
+ public void AIServiceOptions_SectionName_ShouldBeAI()
+ {
+ // Assert
+ AIServiceOptions.SectionName.Should().Be("AI");
+ }
+
+ [Fact]
+ public void AIServiceOptions_ShouldAllowCustomConfiguration()
+ {
+ // Arrange & Act
+ var options = new AIServiceOptions
+ {
+ Provider = "AzureOpenAI",
+ Model = "gpt-3.5-turbo",
+ MaxHistoryMessages = 50,
+ MaxTokens = 2000,
+ Temperature = 0.5f,
+ SystemPrompt = "Custom prompt"
+ };
+
+ // Assert
+ options.Provider.Should().Be("AzureOpenAI");
+ options.Model.Should().Be("gpt-3.5-turbo");
+ options.MaxHistoryMessages.Should().Be(50);
+ options.MaxTokens.Should().Be(2000);
+ options.Temperature.Should().BeApproximately(0.5f, 0.001f);
+ options.SystemPrompt.Should().Be("Custom prompt");
+ }
+}
diff --git a/services/chat-service-net/tests/ChatService.UnitTests/Domain/Contracts/ChatHubClientTests.cs b/services/chat-service-net/tests/ChatService.UnitTests/Domain/Contracts/ChatHubClientTests.cs
new file mode 100644
index 00000000..5d9c00b9
--- /dev/null
+++ b/services/chat-service-net/tests/ChatService.UnitTests/Domain/Contracts/ChatHubClientTests.cs
@@ -0,0 +1,66 @@
+using ChatService.Domain.Contracts;
+using FluentAssertions;
+using Xunit;
+
+namespace ChatService.UnitTests.Domain.Contracts;
+
+///
+/// EN: Unit tests for IChatHubClient interface and related DTOs.
+/// VI: Unit tests cho IChatHubClient interface và các DTOs liên quan.
+///
+public class ChatHubClientTests
+{
+ [Fact]
+ public void MessageNotification_ShouldCreateWithAllProperties()
+ {
+ // Arrange
+ var id = Guid.NewGuid();
+ var conversationId = Guid.NewGuid();
+ var senderId = "user-123";
+ var senderName = "John Doe";
+ var content = "Hello, World!";
+ var messageType = "text";
+ var sentAt = DateTime.UtcNow;
+ var replyToMessageId = Guid.NewGuid();
+
+ // Act
+ var notification = new MessageNotification(
+ Id: id,
+ ConversationId: conversationId,
+ SenderId: senderId,
+ SenderName: senderName,
+ Content: content,
+ MessageType: messageType,
+ SentAt: sentAt,
+ ReplyToMessageId: replyToMessageId
+ );
+
+ // Assert
+ notification.Id.Should().Be(id);
+ notification.ConversationId.Should().Be(conversationId);
+ notification.SenderId.Should().Be(senderId);
+ notification.SenderName.Should().Be(senderName);
+ notification.Content.Should().Be(content);
+ notification.MessageType.Should().Be(messageType);
+ notification.SentAt.Should().Be(sentAt);
+ notification.ReplyToMessageId.Should().Be(replyToMessageId);
+ }
+
+ [Fact]
+ public void MessageNotification_ShouldAllowNullReplyToMessageId()
+ {
+ // Arrange & Act
+ var notification = new MessageNotification(
+ Id: Guid.NewGuid(),
+ ConversationId: Guid.NewGuid(),
+ SenderId: "user-123",
+ SenderName: "John Doe",
+ Content: "Hello!",
+ MessageType: "text",
+ SentAt: DateTime.UtcNow
+ );
+
+ // Assert
+ notification.ReplyToMessageId.Should().BeNull();
+ }
+}
diff --git a/services/chat-service-net/tests/ChatService.UnitTests/Domain/Events/ConversationDomainEventsTests.cs b/services/chat-service-net/tests/ChatService.UnitTests/Domain/Events/ConversationDomainEventsTests.cs
new file mode 100644
index 00000000..a519fa45
--- /dev/null
+++ b/services/chat-service-net/tests/ChatService.UnitTests/Domain/Events/ConversationDomainEventsTests.cs
@@ -0,0 +1,106 @@
+using ChatService.Domain.Events;
+using FluentAssertions;
+using Xunit;
+
+namespace ChatService.UnitTests.Domain.Events;
+
+///
+/// EN: Unit tests for Conversation Domain Events.
+/// VI: Unit tests cho Conversation Domain Events.
+///
+public class ConversationDomainEventsTests
+{
+ [Fact]
+ public void UserJoinedRoomDomainEvent_ShouldCreateWithCorrectProperties()
+ {
+ // Arrange
+ var userId = Guid.NewGuid();
+ var conversationId = Guid.NewGuid();
+ var userName = "John Doe";
+
+ // Act
+ var domainEvent = new UserJoinedRoomDomainEvent(userId, conversationId, userName);
+
+ // Assert
+ domainEvent.UserId.Should().Be(userId);
+ domainEvent.ConversationId.Should().Be(conversationId);
+ domainEvent.UserName.Should().Be(userName);
+ domainEvent.JoinedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
+ }
+
+ [Fact]
+ public void UserLeftRoomDomainEvent_ShouldCreateWithCorrectProperties()
+ {
+ // Arrange
+ var userId = Guid.NewGuid();
+ var conversationId = Guid.NewGuid();
+
+ // Act
+ var domainEvent = new UserLeftRoomDomainEvent(userId, conversationId);
+
+ // Assert
+ domainEvent.UserId.Should().Be(userId);
+ domainEvent.ConversationId.Should().Be(conversationId);
+ domainEvent.LeftAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
+ }
+
+ [Fact]
+ public void TypingDomainEvent_ShouldCreateWithCorrectProperties_WhenTyping()
+ {
+ // Arrange
+ var userId = Guid.NewGuid();
+ var conversationId = Guid.NewGuid();
+ var userName = "Jane Smith";
+ var isTyping = true;
+
+ // Act
+ var domainEvent = new TypingDomainEvent(userId, conversationId, userName, isTyping);
+
+ // Assert
+ domainEvent.UserId.Should().Be(userId);
+ domainEvent.ConversationId.Should().Be(conversationId);
+ domainEvent.UserName.Should().Be(userName);
+ domainEvent.IsTyping.Should().BeTrue();
+ }
+
+ [Fact]
+ public void TypingDomainEvent_ShouldCreateWithCorrectProperties_WhenStoppedTyping()
+ {
+ // Arrange
+ var userId = Guid.NewGuid();
+ var conversationId = Guid.NewGuid();
+ var userName = "Jane Smith";
+ var isTyping = false;
+
+ // Act
+ var domainEvent = new TypingDomainEvent(userId, conversationId, userName, isTyping);
+
+ // Assert
+ domainEvent.IsTyping.Should().BeFalse();
+ }
+
+ [Fact]
+ public void MessageSentDomainEvent_ShouldImplementINotification()
+ {
+ // Assert
+ typeof(MessageSentDomainEvent).Should().Implement();
+ }
+
+ [Fact]
+ public void MessageReadDomainEvent_ShouldCreateWithCorrectProperties()
+ {
+ // Arrange
+ var messageId = Guid.NewGuid();
+ var conversationId = Guid.NewGuid();
+ var readerId = Guid.NewGuid();
+
+ // Act
+ var domainEvent = new MessageReadDomainEvent(messageId, conversationId, readerId);
+
+ // Assert
+ domainEvent.MessageId.Should().Be(messageId);
+ domainEvent.ConversationId.Should().Be(conversationId);
+ domainEvent.ReaderId.Should().Be(readerId);
+ domainEvent.ReadAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
+ }
+}
diff --git a/services/chat-service-net/tests/ChatService.UnitTests/Infrastructure/Services/AIServiceTests.cs b/services/chat-service-net/tests/ChatService.UnitTests/Infrastructure/Services/AIServiceTests.cs
new file mode 100644
index 00000000..ea45a379
--- /dev/null
+++ b/services/chat-service-net/tests/ChatService.UnitTests/Infrastructure/Services/AIServiceTests.cs
@@ -0,0 +1,152 @@
+using ChatService.Domain.Contracts;
+using ChatService.Infrastructure.Services;
+using FluentAssertions;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Moq;
+using Xunit;
+
+namespace ChatService.UnitTests.Infrastructure.Services;
+
+///
+/// EN: Unit tests for NullAIService (used when OpenAI API key is not configured).
+/// VI: Unit tests cho NullAIService (dùng khi chưa cấu hình OpenAI API key).
+///
+public class NullAIServiceTests
+{
+ private readonly NullAIService _sut;
+
+ public NullAIServiceTests()
+ {
+ _sut = new NullAIService();
+ }
+
+ [Fact]
+ public async Task IsAvailableAsync_ShouldReturnFalse()
+ {
+ // Act
+ var result = await _sut.IsAvailableAsync();
+
+ // Assert
+ result.Should().BeFalse();
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_ShouldReturnNotConfiguredMessage()
+ {
+ // Arrange
+ var prompt = "Hello AI";
+ var history = Enumerable.Empty();
+
+ // Act
+ var result = await _sut.GetResponseAsync(prompt, history);
+
+ // Assert
+ result.Should().Contain("not configured");
+ }
+
+ [Fact]
+ public async Task StreamResponseAsync_ShouldReturnNotConfiguredMessage()
+ {
+ // Arrange
+ var prompt = "Hello AI";
+ var history = Enumerable.Empty();
+ var chunks = new List();
+
+ // Act
+ await foreach (var chunk in _sut.StreamResponseAsync(prompt, history))
+ {
+ chunks.Add(chunk);
+ }
+
+ // Assert
+ chunks.Should().HaveCount(1);
+ chunks[0].Should().Contain("not configured");
+ }
+
+ [Fact]
+ public void NullAIService_ShouldImplementIAIService()
+ {
+ // Assert
+ _sut.Should().BeAssignableTo();
+ }
+}
+
+///
+/// EN: Unit tests for AIService (requires mocking HttpClient).
+/// VI: Unit tests cho AIService (cần mock HttpClient).
+///
+public class AIServiceTests
+{
+ [Fact]
+ public void AIService_ShouldImplementIAIService()
+ {
+ // Arrange
+ var httpClient = new HttpClient();
+ var options = Options.Create(new AIServiceOptions());
+ var logger = new Mock>().Object;
+
+ // Act
+ var service = new AIService(httpClient, options, logger);
+
+ // Assert
+ service.Should().BeAssignableTo();
+ }
+
+ [Fact]
+ public void AIService_ShouldUseProvidedOptions()
+ {
+ // Arrange
+ var httpClient = new HttpClient();
+ var options = Options.Create(new AIServiceOptions
+ {
+ Model = "gpt-3.5-turbo",
+ MaxHistoryMessages = 10
+ });
+ var logger = new Mock>().Object;
+
+ // Act
+ var service = new AIService(httpClient, options, logger);
+
+ // Assert
+ service.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task IsAvailableAsync_ShouldReturnFalse_WhenApiReturnsError()
+ {
+ // Arrange
+ var mockHandler = new MockHttpMessageHandler(new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized));
+ var httpClient = new HttpClient(mockHandler);
+ var options = Options.Create(new AIServiceOptions());
+ var logger = new Mock>().Object;
+ var service = new AIService(httpClient, options, logger);
+
+ // Act
+ var result = await service.IsAvailableAsync();
+
+ // Assert
+ result.Should().BeFalse();
+ }
+}
+
+///
+/// EN: Mock HTTP message handler for testing.
+/// VI: Mock HTTP message handler cho testing.
+///
+internal class MockHttpMessageHandler : HttpMessageHandler
+{
+ private readonly HttpResponseMessage _response;
+
+ public MockHttpMessageHandler(HttpResponseMessage response)
+ {
+ _response = response;
+ }
+
+ protected override Task SendAsync(
+ HttpRequestMessage request,
+ CancellationToken cancellationToken)
+ {
+ return Task.FromResult(_response);
+ }
+}
diff --git a/services/social-service-net/Dockerfile b/services/social-service-net/Dockerfile
index fdbe7e10..277f9f2e 100644
--- a/services/social-service-net/Dockerfile
+++ b/services/social-service-net/Dockerfile
@@ -20,11 +20,11 @@ COPY src/ ./src/
# EN: Build the application
# VI: Build ứng dụng
WORKDIR "/src/src/SocialService.API"
-RUN dotnet build "SocialService.API.csproj" -c Release -o /app/build --no-restore
+RUN dotnet build "SocialService.API.csproj" -c Release -o /app/build
# Publish stage / Giai đoạn publish
FROM build AS publish
-RUN dotnet publish "SocialService.API.csproj" -c Release -o /app/publish /p:UseAppHost=false --no-restore
+RUN dotnet publish "SocialService.API.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Runtime stage / Giai đoạn runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
diff --git a/services/social-service-net/docs/en/ARCHITECTURE.md b/services/social-service-net/docs/en/ARCHITECTURE.md
index bc306599..3f187b44 100644
--- a/services/social-service-net/docs/en/ARCHITECTURE.md
+++ b/services/social-service-net/docs/en/ARCHITECTURE.md
@@ -1,45 +1,54 @@
-# Architecture Documentation
+# Architecture Documentation - Social Service
-> Detailed architecture documentation for the .NET 10 Microservice Template.
+> Detailed architecture documentation for Social Service - Social relationship management service.
## Architecture Overview
```mermaid
graph TB
subgraph "API Layer"
- C[Controllers]
+ RC[RelationshipsController]
+ BC[BlocksController]
CMD[Commands]
Q[Queries]
B[Behaviors]
- V[Validations]
end
subgraph "Domain Layer"
- AR[Aggregate Roots]
- E[Entities]
- VO[Value Objects]
+ RA[Relationship Aggregate]
+ UBA[UserBlock Aggregate]
+ UPA[UserProfile Aggregate]
DE[Domain Events]
- DX[Domain Exceptions]
end
subgraph "Infrastructure Layer"
DB[(PostgreSQL)]
- R[Repositories]
- CTX[DbContext]
- ID[Idempotency]
+ RR[RelationshipRepository]
+ UBR[UserBlockRepository]
+ UPR[UserProfileRepository]
+ CTX[SocialServiceContext]
end
- C --> CMD
- C --> Q
- CMD --> B --> V
- CMD --> AR
- Q --> R
- R --> CTX --> DB
- AR --> DE
- R --> AR
+ RC --> CMD
+ RC --> Q
+ BC --> CMD
+ BC --> Q
+ CMD --> B
+ CMD --> RA
+ CMD --> UBA
+ Q --> RR
+ Q --> UBR
+ RR --> CTX --> DB
+ UBR --> CTX
+ UPR --> CTX
+ RA --> DE
+ UBA --> DE
- style C fill:#4a90d9,stroke:#2d5986,color:#fff
- style AR fill:#50c878,stroke:#2d8659,color:#fff
+ style RC fill:#4a90d9,stroke:#2d5986,color:#fff
+ style BC fill:#4a90d9,stroke:#2d5986,color:#fff
+ style RA fill:#50c878,stroke:#2d8659,color:#fff
+ style UBA fill:#50c878,stroke:#2d8659,color:#fff
+ style UPA fill:#50c878,stroke:#2d8659,color:#fff
style DB fill:#ff6b6b,stroke:#c0392b,color:#fff
```
@@ -47,34 +56,51 @@ graph TB
### 1. Domain Layer (SocialService.Domain)
-The heart of the application containing pure business logic. This layer:
-- Has **ZERO** external dependencies (except MediatR.Contracts for events)
-- Contains only POCO classes
-- Implements DDD tactical patterns
+The heart of the application containing pure business logic.
-#### Components
+#### Aggregates
-| Component | Purpose |
+| Aggregate | Purpose |
|-----------|---------|
-| **SeedWork** | Base classes: Entity, ValueObject, Enumeration, IAggregateRoot |
-| **AggregatesModel** | Aggregate roots with their entities and value objects |
-| **Events** | Domain events for cross-aggregate communication |
-| **Exceptions** | Domain-specific exceptions for business rule violations |
+| **Relationship** | Manages friendship and following |
+| **UserBlock** | Manages user blocking |
+| **UserProfile** | Caches user info from IAM Service |
+
+#### Domain Events
+
+| Event | Trigger |
+|-------|---------|
+| `FriendRequestSentDomainEvent` | Friend request sent |
+| `FriendshipCreatedDomainEvent` | Friend request accepted |
+| `UserFollowedDomainEvent` | User followed |
+| `RelationshipStatusChangedDomainEvent` | Relationship status changed |
+| `RelationshipRemovedDomainEvent` | Unfriend/unfollow |
+| `UserBlockedDomainEvent` | User blocked |
+| `UserUnblockedDomainEvent` | User unblocked |
### 2. Infrastructure Layer (SocialService.Infrastructure)
-Technical implementations and external concerns:
-- Database access (EF Core)
-- Repository implementations
-- External service integrations
+Technical implementation and data access.
+
+| Component | Purpose |
+|-----------|---------|
+| **SocialServiceContext** | DbContext with Unit of Work pattern |
+| **RelationshipRepository** | CRUD for Relationship aggregate |
+| **UserBlockRepository** | CRUD for UserBlock aggregate |
+| **UserProfileRepository** | CRUD for UserProfile aggregate |
+| **EntityConfigurations** | EF Core Fluent API mappings |
### 3. API Layer (SocialService.API)
-Application entry point and CQRS implementation:
-- Controllers for HTTP handling
-- Commands for write operations
-- Queries for read operations
-- MediatR behaviors for cross-cutting concerns
+Application entry point and CQRS implementation.
+
+| Component | Purpose |
+|-----------|---------|
+| **RelationshipsController** | APIs for friendship and following |
+| **BlocksController** | APIs for block/unblock users |
+| **Commands** | 6 write operations (MediatR) |
+| **Queries** | 4 read operations |
+| **Behaviors** | Logging, Validation, Transaction |
## CQRS Flow
@@ -86,7 +112,7 @@ sequenceDiagram
participant LoggingBehavior
participant ValidatorBehavior
participant TransactionBehavior
- participant CommandHandler
+ participant Handler
participant Repository
participant DbContext
@@ -95,94 +121,118 @@ sequenceDiagram
MediatR->>LoggingBehavior: Handle
LoggingBehavior->>ValidatorBehavior: Next()
ValidatorBehavior->>TransactionBehavior: Next()
- TransactionBehavior->>CommandHandler: Next()
- CommandHandler->>Repository: Add/Update/Delete
+ TransactionBehavior->>Handler: Next()
+ Handler->>Repository: Add/Update/Delete
Repository->>DbContext: SaveEntitiesAsync()
- DbContext-->>Repository: Success
- Repository-->>CommandHandler: Result
- CommandHandler-->>Controller: Response
+ DbContext-->>Repository: Success + Dispatch Events
+ Repository-->>Handler: Result
+ Handler-->>Controller: Response
Controller-->>Client: HTTP Response
```
-## Domain Events
+## Relationship State Machine
```mermaid
-graph LR
- AR[Aggregate Root] -->|Raises| DE[Domain Event]
- DE -->|Dispatched by| CTX[DbContext]
- CTX -->|Publishes to| M[MediatR]
- M -->|Handled by| H1[Handler 1]
- M -->|Handled by| H2[Handler 2]
+stateDiagram-v2
+ [*] --> Pending: SendFriendRequest
+ Pending --> Accepted: Accept()
+ Pending --> Rejected: Reject()
+ Pending --> Cancelled: Cancel()
+ Accepted --> Cancelled: Remove()
- style AR fill:#50c878,stroke:#2d8659,color:#fff
- style DE fill:#f39c12,stroke:#d68910,color:#fff
- style M fill:#9b59b6,stroke:#7d3c98,color:#fff
+ [*] --> Accepted: FollowUser (auto-accept)
+
+ note right of Pending: For Friendship type only
+ note right of Accepted: Following is auto-Accepted
```
## Database Schema
-### Sample Aggregate
-
```mermaid
erDiagram
- samples {
+ relationships {
uuid id PK
- varchar(200) name
- varchar(1000) description
+ uuid requester_id FK
+ uuid addressee_id FK
+ int type_id FK
int status_id FK
timestamp created_at
timestamp updated_at
}
- sample_statuses {
+ relationship_types {
int id PK
varchar(50) name
}
- samples ||--o{ sample_statuses : has
+ relationship_statuses {
+ int id PK
+ varchar(50) name
+ }
+
+ user_blocks {
+ uuid id PK
+ uuid blocker_id FK
+ uuid blocked_id FK
+ varchar(500) reason
+ timestamp created_at
+ }
+
+ user_profiles {
+ uuid id PK
+ uuid user_id UK
+ varchar(100) display_name
+ varchar(500) avatar_url
+ varchar(500) bio
+ timestamp last_synced_at
+ timestamp created_at
+ }
+
+ relationships ||--o| relationship_types : has_type
+ relationships ||--o| relationship_statuses : has_status
```
-## MediatR Pipeline
+### Enumeration Values
-```
-Request → LoggingBehavior → ValidatorBehavior → TransactionBehavior → Handler → Response
- │ │ │
- ▼ ▼ ▼
- Log start/end Validate Begin/Commit
- + timing with Transaction
- FluentValidation
-```
+**RelationshipType:**
+| ID | Name |
+|----|------|
+| 1 | Friendship |
+| 2 | Following |
-### Behavior Order
+**RelationshipStatus:**
+| ID | Name |
+|----|------|
+| 1 | Pending |
+| 2 | Accepted |
+| 3 | Rejected |
+| 4 | Cancelled |
-1. **LoggingBehavior** - Logs request handling with timing
-2. **ValidatorBehavior** - Validates request using FluentValidation
-3. **TransactionBehavior** - Wraps command handlers in database transactions
+## Domain Events Flow
-## Error Handling
-
-### Exception Hierarchy
-
-```
-Exception
-└── DomainException
- └── SampleDomainException
-```
-
-### Problem Details (RFC 7807)
-
-All errors are returned in Problem Details format:
-
-```json
-{
- "type": "https://tools.ietf.org/html/rfc7807",
- "title": "Validation Error",
- "status": 400,
- "detail": "One or more validation errors occurred.",
- "errors": {
- "Name": ["Name is required"]
- }
-}
+```mermaid
+graph LR
+ subgraph "Relationship Aggregate"
+ R[Relationship] -->|Creates| E1[FriendRequestSentEvent]
+ R -->|Accept| E2[FriendshipCreatedEvent]
+ R -->|Follow| E3[UserFollowedEvent]
+ R -->|Any Change| E4[StatusChangedEvent]
+ R -->|Remove| E5[RemovedEvent]
+ end
+
+ subgraph "UserBlock Aggregate"
+ UB[UserBlock] -->|Creates| E6[UserBlockedEvent]
+ end
+
+ subgraph "Event Handlers"
+ E1 --> H1[Notification Handler]
+ E2 --> H2[Notification Handler]
+ E3 --> H3[Feed Handler]
+ E6 --> H4[Cleanup Handler]
+ end
+
+ style R fill:#50c878,stroke:#2d8659,color:#fff
+ style UB fill:#50c878,stroke:#2d8659,color:#fff
```
## Health Checks
@@ -250,18 +300,38 @@ spec:
## Security Considerations
-1. **Authentication**: JWT Bearer token (configure in production)
-2. **Authorization**: Role-based access control
+1. **Authentication**: JWT Bearer token from IAM Service
+2. **Authorization**: Ownership check in business logic
3. **Input Validation**: FluentValidation on all requests
-4. **SQL Injection**: EF Core parameterized queries
-5. **Secrets**: Environment variables, never in code
+4. **Block Enforcement**: Blocking a user prevents all interactions
+5. **SQL Injection**: EF Core parameterized queries
+
+## Integration with Other Services
+
+```mermaid
+graph LR
+ SS[Social Service] <-->|Integration Events| EB[Event Bus]
+ EB <--> IAM[IAM Service]
+ EB <--> NS[Notification Service]
+ EB <--> FS[Feed Service]
+
+ IAM -->|UserCreated/Updated| SS
+ SS -->|FriendshipCreated| NS
+ SS -->|UserFollowed| FS
+
+ style SS fill:#4a90d9,stroke:#2d5986,color:#fff
+ style EB fill:#9b59b6,stroke:#7d3c98,color:#fff
+```
## Performance Optimization
1. **Connection Pooling**: EF Core with Npgsql connection resilience
2. **Async/Await**: All I/O operations are async
-3. **Response Caching**: Add caching headers for queries
-4. **Database Indexes**: Configure in EntityConfigurations
+3. **Pagination**: All list queries support skip/take
+4. **Indexes**:
+ - `relationships`: (requester_id, addressee_id, type_id)
+ - `user_blocks`: (blocker_id, blocked_id)
+ - `user_profiles`: (user_id) UNIQUE
## References
diff --git a/services/social-service-net/docs/en/README.md b/services/social-service-net/docs/en/README.md
index d1baaa64..b1d512b5 100644
--- a/services/social-service-net/docs/en/README.md
+++ b/services/social-service-net/docs/en/README.md
@@ -1,19 +1,25 @@
-# .NET 10 Microservice Template
+# Social Service - Social Relationship Management
-> Enterprise-grade .NET 10 microservice template following DDD, CQRS, and Clean Architecture patterns.
+> .NET 10 microservice for managing social relationships between users: friendship, following, and blocking.
## Overview
-This template provides a production-ready structure for .NET microservices based on the eShopOnContainers reference architecture with:
+Social Service manages all social relationships between users in the GoodGo Platform:
-- **Domain-Driven Design (DDD)** - Aggregates, Entities, Value Objects, Domain Events
-- **CQRS Pattern** - Separate Commands (write) and Queries (read) with MediatR
-- **Clean Architecture** - Domain, Infrastructure, API layered separation
-- **EF Core 10** - PostgreSQL with connection resilience
-- **FluentValidation** - Request validation
-- **API Versioning** - URL segment versioning
-- **Health Checks** - Kubernetes-ready probes
-- **Structured Logging** - Serilog with console and Seq
+- **Friendship** - Send/accept/reject friend requests, unfriend
+- **Following** - Follow/unfollow users
+- **Blocking** - Block/unblock users with optional reason
+- **User Profile** - Sync user info from IAM Service
+
+### Architecture & Patterns
+
+| Pattern | Implementation |
+|---------|---------------|
+| **DDD** | Aggregates, Entities, Value Objects, Domain Events |
+| **CQRS** | Commands/Queries separated with MediatR |
+| **Clean Architecture** | 3-layer: Domain, Infrastructure, API |
+| **EF Core 10** | PostgreSQL with connection resilience |
+| **Structured Logging** | Serilog with console and Seq |
## Prerequisites
@@ -21,41 +27,11 @@ This template provides a production-ready structure for .NET microservices based
|-------------|---------|
| .NET SDK | 10.0.101+ |
| Docker | 24.0+ |
-| PostgreSQL | 15+ (or use Docker) |
-
-```bash
-# Check .NET version
-dotnet --version
-# Should output: 10.0.xxx
-```
+| PostgreSQL | 15+ |
## Quick Start
-### 1. Create New Service
-
-```bash
-# Copy template to new service
-cp -r services/_template_dot_net services/your-service-name
-
-# Navigate to service directory
-cd services/your-service-name
-
-# Rename all occurrences of "SocialService" to "YourService"
-find . -type f -name "*.cs" -exec sed -i '' 's/SocialService/YourService/g' {} +
-find . -type f -name "*.csproj" -exec sed -i '' 's/SocialService/YourService/g' {} +
-```
-
-### 2. Configure Environment
-
-```bash
-# Copy environment template
-cp .env.example .env
-
-# Edit with your configuration
-nano .env
-```
-
-### 3. Run with Docker
+### Run with Docker
```bash
# Start all services (API + PostgreSQL + Redis)
@@ -65,7 +41,7 @@ docker-compose up -d
docker-compose logs -f socialservice-api
```
-### 4. Run Locally
+### Run Locally
```bash
# Restore dependencies
@@ -78,53 +54,101 @@ dotnet build
dotnet run --project src/SocialService.API
```
-## Project Structure
+## Domain Model
+### Aggregates
+
+#### 1. Relationship Aggregate
+Manages friendship and following relationships between users.
+
+```mermaid
+classDiagram
+ class Relationship {
+ +Guid Id
+ +Guid RequesterId
+ +Guid AddresseeId
+ +RelationshipType Type
+ +RelationshipStatus Status
+ +DateTime CreatedAt
+ +DateTime? UpdatedAt
+ +Accept()
+ +Reject()
+ +Cancel()
+ +Remove()
+ }
+
+ class RelationshipType {
+ <>
+ Friendship
+ Following
+ }
+
+ class RelationshipStatus {
+ <>
+ Pending
+ Accepted
+ Rejected
+ Cancelled
+ }
+
+ Relationship --> RelationshipType
+ Relationship --> RelationshipStatus
```
-_template_dot_net/
-├── src/
-│ ├── SocialService.API/ # Presentation Layer (Controllers, CQRS)
-│ │ ├── Controllers/ # API endpoints
-│ │ ├── Application/ # CQRS Implementation
-│ │ │ ├── Commands/ # Write operations (MediatR)
-│ │ │ ├── Queries/ # Read operations
-│ │ │ ├── Behaviors/ # MediatR pipeline behaviors
-│ │ │ └── Validations/ # FluentValidation validators
-│ │ ├── Middleware/ # Custom middleware
-│ │ └── Program.cs # Application entry point
-│ │
-│ ├── SocialService.Domain/ # Domain Layer (Pure business logic)
-│ │ ├── AggregatesModel/ # Aggregate roots and entities
-│ │ ├── Events/ # Domain events
-│ │ ├── Exceptions/ # Domain exceptions
-│ │ └── SeedWork/ # Base classes (Entity, ValueObject, etc.)
-│ │
-│ └── SocialService.Infrastructure/ # Infrastructure Layer (Data access)
-│ ├── EntityConfigurations/ # EF Core Fluent API configurations
-│ ├── Repositories/ # Repository implementations
-│ ├── Idempotency/ # Request idempotency handling
-│ └── SocialServiceContext.cs # DbContext with Unit of Work
-│
-├── tests/
-│ ├── SocialService.UnitTests/ # Unit tests (Domain, Application)
-│ └── SocialService.FunctionalTests/ # Integration tests (API endpoints)
-│
-├── Dockerfile # Multi-stage Docker build
-├── docker-compose.yml # Local development setup
-├── global.json # .NET SDK version pinning
-└── Directory.Build.props # Common MSBuild properties
-```
+
+**Business Rules:**
+- Following is auto-accepted
+- Friendship requires confirmation from recipient
+- Cannot create relationship with yourself
+- Only Pending requests can be Accept/Reject/Cancel
+- Only Accepted relationships can be Removed
+
+#### 2. UserBlock Aggregate
+Manages user blocking.
+
+| Field | Description |
+|-------|-------------|
+| `BlockerId` | ID of blocking user |
+| `BlockedId` | ID of blocked user |
+| `Reason` | Block reason (optional) |
+| `CreatedAt` | Block timestamp |
+
+#### 3. UserProfile Aggregate
+Caches user info synced from IAM Service.
+
+| Field | Description |
+|-------|-------------|
+| `UserId` | User ID from IAM |
+| `DisplayName` | Display name |
+| `AvatarUrl` | Avatar URL |
+| `Bio` | User bio |
+| `LastSyncedAt` | Last sync time |
## API Endpoints
+### Relationships - Friendship
+
| Method | Endpoint | Description |
|--------|----------|-------------|
-| `GET` | `/api/v1/samples` | Get all samples |
-| `GET` | `/api/v1/samples/{id}` | Get sample by ID |
-| `POST` | `/api/v1/samples` | Create new sample |
-| `PUT` | `/api/v1/samples/{id}` | Update sample |
-| `DELETE` | `/api/v1/samples/{id}` | Delete sample |
-| `PATCH` | `/api/v1/samples/{id}/status` | Change status |
+| `GET` | `/api/v1/relationships/users/{userId}/friends` | Get friend list |
+| `POST` | `/api/v1/relationships/friend-requests` | Send friend request |
+| `PUT` | `/api/v1/relationships/friend-requests/{id}` | Respond (accept/reject) |
+| `GET` | `/api/v1/relationships/users/{id1}/mutual-friends/{id2}` | Get mutual friends |
+| `GET` | `/api/v1/relationships/users/{userId}/suggestions` | Friend suggestions |
+
+### Relationships - Following
+
+| Method | Endpoint | Description |
+|--------|----------|-------------|
+| `POST` | `/api/v1/relationships/follow` | Follow user |
+| `DELETE` | `/api/v1/relationships/follow` | Unfollow user |
+
+### Block Users
+
+| Method | Endpoint | Description |
+|--------|----------|-------------|
+| `POST` | `/api/v1/blocks` | Block user |
+| `DELETE` | `/api/v1/blocks` | Unblock user |
+| `GET` | `/api/v1/blocks/users/{userId}` | Get blocked users list |
### Health Endpoints
@@ -134,74 +158,79 @@ _template_dot_net/
| `/health/live` | Liveness probe |
| `/health/ready` | Readiness probe |
+## Project Structure
+
+```
+social-service-net/
+├── src/
+│ ├── SocialService.API/
+│ │ ├── Controllers/
+│ │ │ ├── RelationshipsController.cs # Friends + Following APIs
+│ │ │ └── BlocksController.cs # Block/Unblock APIs
+│ │ ├── Application/
+│ │ │ ├── Commands/ # 6 commands
+│ │ │ ├── Queries/ # 4 queries
+│ │ │ ├── Behaviors/ # MediatR pipeline
+│ │ │ └── Validations/ # FluentValidation
+│ │ └── Program.cs
+│ │
+│ ├── SocialService.Domain/
+│ │ ├── AggregatesModel/
+│ │ │ ├── RelationshipAggregate/ # Relationship, Status, Type
+│ │ │ ├── UserBlockAggregate/ # UserBlock
+│ │ │ └── UserProfileAggregate/ # UserProfile (IAM sync)
+│ │ ├── Events/ # 7 domain events
+│ │ ├── Exceptions/ # SocialDomainException
+│ │ └── SeedWork/ # Base classes
+│ │
+│ └── SocialService.Infrastructure/
+│ ├── EntityConfigurations/ # EF Core mappings
+│ ├── Repositories/ # 3 repositories
+│ ├── Idempotency/ # Request deduplication
+│ └── SocialServiceContext.cs # DbContext
+│
+├── tests/
+│ ├── SocialService.UnitTests/
+│ └── SocialService.FunctionalTests/
+│
+├── Dockerfile
+├── docker-compose.yml
+└── docs/
+```
+
## CQRS Pattern
### Commands (Write Operations)
-```csharp
-// Define command
-public record CreateSampleCommand(string Name, string? Description)
- : IRequest;
-
-// Handle command
-public class CreateSampleCommandHandler : IRequestHandler
-{
- public async Task Handle(CreateSampleCommand request, CancellationToken ct)
- {
- var sample = new Sample(request.Name, request.Description);
- _repository.Add(sample);
- await _repository.UnitOfWork.SaveEntitiesAsync(ct);
- return new CreateSampleCommandResult(sample.Id);
- }
-}
-```
+| Command | Description |
+|---------|-------------|
+| `SendFriendRequestCommand` | Send friend request |
+| `RespondToFriendRequestCommand` | Accept/Reject friend request |
+| `FollowUserCommand` | Follow user |
+| `UnfollowUserCommand` | Unfollow user |
+| `BlockUserCommand` | Block user with optional reason |
+| `UnblockUserCommand` | Unblock user |
### Queries (Read Operations)
-```csharp
-// Define query
-public record GetSampleQuery(Guid SampleId) : IRequest;
-```
+| Query | Description |
+|-------|-------------|
+| `GetFriendsQuery` | Friend list with pagination |
+| `GetMutualFriendsQuery` | Mutual friends between 2 users |
+| `GetFriendSuggestionsQuery` | Friend suggestions |
+| `GetBlockedUsersQuery` | Blocked users list |
-## Domain Model
+## Domain Events
-### Aggregate Root
-
-```csharp
-public class Sample : Entity, IAggregateRoot
-{
- public string Name => _name;
- public SampleStatus Status => _status;
-
- public Sample(string name, string? description) {
- // Business logic validation
- if (string.IsNullOrWhiteSpace(name))
- throw new SampleDomainException("Sample name cannot be empty");
-
- // Domain event
- AddDomainEvent(new SampleCreatedDomainEvent(this));
- }
-
- public void Activate() {
- if (_status != SampleStatus.Draft)
- throw new SampleDomainException("Only draft samples can be activated");
- // State transition
- }
-}
-```
-
-## Testing
-
-```bash
-# Run all tests
-dotnet test
-
-# Run with coverage
-dotnet test /p:CollectCoverage=true /p:CoverageReportFormat=cobertura
-
-# Run specific test project
-dotnet test tests/SocialService.UnitTests
-```
+| Event | Trigger |
+|-------|---------|
+| `FriendRequestSentDomainEvent` | Friend request sent |
+| `FriendshipCreatedDomainEvent` | Friend request accepted |
+| `UserFollowedDomainEvent` | User followed |
+| `RelationshipStatusChangedDomainEvent` | Status changed |
+| `RelationshipRemovedDomainEvent` | Unfriend/unfollow |
+| `UserBlockedDomainEvent` | User blocked |
+| `UserUnblockedDomainEvent` | User unblocked |
## Configuration
@@ -212,7 +241,6 @@ dotnet test tests/SocialService.UnitTests
| `ASPNETCORE_ENVIRONMENT` | Environment name | `Development` |
| `DATABASE_URL` | PostgreSQL connection string | - |
| `REDIS_URL` | Redis connection string | - |
-| `JWT_SECRET` | JWT signing secret (min 32 chars) | - |
### appsettings.json
@@ -227,6 +255,19 @@ dotnet test tests/SocialService.UnitTests
}
```
+## Testing
+
+```bash
+# Run all tests
+dotnet test
+
+# Run with coverage
+dotnet test /p:CollectCoverage=true
+
+# Run specific test project
+dotnet test tests/SocialService.UnitTests
+```
+
## Deployment
### Docker Build
@@ -239,26 +280,11 @@ docker build -t socialservice:latest .
docker run -p 5000:8080 --env-file .env socialservice:latest
```
-### Kubernetes
-
-See [ARCHITECTURE.md](./ARCHITECTURE.md) for Kubernetes deployment manifests.
-
-## What's New in .NET 10
-
-- **C# 14** language features
-- Improved **Native AOT** support
-- Better **async/await** performance
-- Enhanced **JSON serialization**
-- Performance improvements across the board
-- 3-year **LTS** support (until November 2028)
-
## Resources
+- [Architecture Documentation](./ARCHITECTURE.md)
- [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) - Reference architecture
-- [.NET 10 Documentation](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10)
- [DDD with .NET](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/)
-- [MediatR](https://github.com/jbogard/MediatR) - CQRS library
-- [FluentValidation](https://docs.fluentvalidation.net/) - Validation library
## License
diff --git a/services/social-service-net/docs/vi/ARCHITECTURE.md b/services/social-service-net/docs/vi/ARCHITECTURE.md
index 98d304d7..d0602c28 100644
--- a/services/social-service-net/docs/vi/ARCHITECTURE.md
+++ b/services/social-service-net/docs/vi/ARCHITECTURE.md
@@ -1,45 +1,54 @@
-# Tài Liệu Kiến Trúc
+# Tài Liệu Kiến Trúc - Social Service
-> Tài liệu kiến trúc chi tiết cho Template Microservice .NET 10.
+> Tài liệu kiến trúc chi tiết cho Social Service - Dịch vụ quản lý quan hệ xã hội.
## Tổng Quan Kiến Trúc
```mermaid
graph TB
subgraph "Lớp API"
- C[Controllers]
+ RC[RelationshipsController]
+ BC[BlocksController]
CMD[Commands]
Q[Queries]
B[Behaviors]
- V[Validations]
end
subgraph "Lớp Domain"
- AR[Aggregate Roots]
- E[Entities]
- VO[Value Objects]
+ RA[Relationship Aggregate]
+ UBA[UserBlock Aggregate]
+ UPA[UserProfile Aggregate]
DE[Domain Events]
- DX[Domain Exceptions]
end
subgraph "Lớp Infrastructure"
DB[(PostgreSQL)]
- R[Repositories]
- CTX[DbContext]
- ID[Idempotency]
+ RR[RelationshipRepository]
+ UBR[UserBlockRepository]
+ UPR[UserProfileRepository]
+ CTX[SocialServiceContext]
end
- C --> CMD
- C --> Q
- CMD --> B --> V
- CMD --> AR
- Q --> R
- R --> CTX --> DB
- AR --> DE
- R --> AR
+ RC --> CMD
+ RC --> Q
+ BC --> CMD
+ BC --> Q
+ CMD --> B
+ CMD --> RA
+ CMD --> UBA
+ Q --> RR
+ Q --> UBR
+ RR --> CTX --> DB
+ UBR --> CTX
+ UPR --> CTX
+ RA --> DE
+ UBA --> DE
- style C fill:#4a90d9,stroke:#2d5986,color:#fff
- style AR fill:#50c878,stroke:#2d8659,color:#fff
+ style RC fill:#4a90d9,stroke:#2d5986,color:#fff
+ style BC fill:#4a90d9,stroke:#2d5986,color:#fff
+ style RA fill:#50c878,stroke:#2d8659,color:#fff
+ style UBA fill:#50c878,stroke:#2d8659,color:#fff
+ style UPA fill:#50c878,stroke:#2d8659,color:#fff
style DB fill:#ff6b6b,stroke:#c0392b,color:#fff
```
@@ -47,34 +56,51 @@ graph TB
### 1. Lớp Domain (SocialService.Domain)
-Trái tim của ứng dụng chứa business logic thuần túy. Lớp này:
-- Có **ZERO** phụ thuộc bên ngoài (ngoại trừ MediatR.Contracts cho events)
-- Chỉ chứa các class POCO
-- Triển khai các tactical patterns của DDD
+Trái tim của ứng dụng chứa business logic thuần túy.
-#### Thành Phần
+#### Aggregates
-| Thành phần | Mục Đích |
-|------------|----------|
-| **SeedWork** | Base classes: Entity, ValueObject, Enumeration, IAggregateRoot |
-| **AggregatesModel** | Aggregate roots với entities và value objects |
-| **Events** | Domain events cho giao tiếp cross-aggregate |
-| **Exceptions** | Domain exceptions cho vi phạm business rules |
+| Aggregate | Mục Đích |
+|-----------|----------|
+| **Relationship** | Quản lý kết bạn (Friendship) và theo dõi (Following) |
+| **UserBlock** | Quản lý việc block users |
+| **UserProfile** | Cache thông tin user từ IAM Service |
+
+#### Domain Events
+
+| Event | Trigger |
+|-------|---------|
+| `FriendRequestSentDomainEvent` | Gửi yêu cầu kết bạn |
+| `FriendshipCreatedDomainEvent` | Chấp nhận kết bạn |
+| `UserFollowedDomainEvent` | Follow user |
+| `RelationshipStatusChangedDomainEvent` | Thay đổi trạng thái quan hệ |
+| `RelationshipRemovedDomainEvent` | Hủy kết bạn/bỏ theo dõi |
+| `UserBlockedDomainEvent` | Block user |
+| `UserUnblockedDomainEvent` | Unblock user |
### 2. Lớp Infrastructure (SocialService.Infrastructure)
-Triển khai kỹ thuật và các mối quan tâm bên ngoài:
-- Truy cập database (EF Core)
-- Triển khai repositories
-- Tích hợp external services
+Triển khai kỹ thuật và truy cập dữ liệu.
+
+| Component | Mục Đích |
+|-----------|----------|
+| **SocialServiceContext** | DbContext với Unit of Work pattern |
+| **RelationshipRepository** | CRUD cho Relationship aggregate |
+| **UserBlockRepository** | CRUD cho UserBlock aggregate |
+| **UserProfileRepository** | CRUD cho UserProfile aggregate |
+| **EntityConfigurations** | EF Core Fluent API mappings |
### 3. Lớp API (SocialService.API)
-Điểm vào ứng dụng và triển khai CQRS:
-- Controllers để xử lý HTTP
-- Commands cho các thao tác ghi
-- Queries cho các thao tác đọc
-- MediatR behaviors cho cross-cutting concerns
+Điểm vào ứng dụng và triển khai CQRS.
+
+| Component | Mục Đích |
+|-----------|----------|
+| **RelationshipsController** | APIs cho kết bạn và theo dõi |
+| **BlocksController** | APIs cho block/unblock users |
+| **Commands** | 6 write operations (MediatR) |
+| **Queries** | 4 read operations |
+| **Behaviors** | Logging, Validation, Transaction |
## Luồng CQRS
@@ -86,7 +112,7 @@ sequenceDiagram
participant LoggingBehavior
participant ValidatorBehavior
participant TransactionBehavior
- participant CommandHandler
+ participant Handler
participant Repository
participant DbContext
@@ -95,94 +121,118 @@ sequenceDiagram
MediatR->>LoggingBehavior: Handle
LoggingBehavior->>ValidatorBehavior: Next()
ValidatorBehavior->>TransactionBehavior: Next()
- TransactionBehavior->>CommandHandler: Next()
- CommandHandler->>Repository: Add/Update/Delete
+ TransactionBehavior->>Handler: Next()
+ Handler->>Repository: Add/Update/Delete
Repository->>DbContext: SaveEntitiesAsync()
- DbContext-->>Repository: Success
- Repository-->>CommandHandler: Result
- CommandHandler-->>Controller: Response
+ DbContext-->>Repository: Success + Dispatch Events
+ Repository-->>Handler: Result
+ Handler-->>Controller: Response
Controller-->>Client: HTTP Response
```
-## Domain Events
+## Relationship State Machine
```mermaid
-graph LR
- AR[Aggregate Root] -->|Phát sinh| DE[Domain Event]
- DE -->|Dispatch bởi| CTX[DbContext]
- CTX -->|Publish tới| M[MediatR]
- M -->|Xử lý bởi| H1[Handler 1]
- M -->|Xử lý bởi| H2[Handler 2]
+stateDiagram-v2
+ [*] --> Pending: SendFriendRequest
+ Pending --> Accepted: Accept()
+ Pending --> Rejected: Reject()
+ Pending --> Cancelled: Cancel()
+ Accepted --> Cancelled: Remove()
- style AR fill:#50c878,stroke:#2d8659,color:#fff
- style DE fill:#f39c12,stroke:#d68910,color:#fff
- style M fill:#9b59b6,stroke:#7d3c98,color:#fff
+ [*] --> Accepted: FollowUser (auto-accept)
+
+ note right of Pending: Chỉ cho Friendship type
+ note right of Accepted: Follow tự động Accepted
```
## Schema Database
-### Sample Aggregate
-
```mermaid
erDiagram
- samples {
+ relationships {
uuid id PK
- varchar(200) name
- varchar(1000) description
+ uuid requester_id FK
+ uuid addressee_id FK
+ int type_id FK
int status_id FK
timestamp created_at
timestamp updated_at
}
- sample_statuses {
+ relationship_types {
int id PK
varchar(50) name
}
- samples ||--o{ sample_statuses : has
+ relationship_statuses {
+ int id PK
+ varchar(50) name
+ }
+
+ user_blocks {
+ uuid id PK
+ uuid blocker_id FK
+ uuid blocked_id FK
+ varchar(500) reason
+ timestamp created_at
+ }
+
+ user_profiles {
+ uuid id PK
+ uuid user_id UK
+ varchar(100) display_name
+ varchar(500) avatar_url
+ varchar(500) bio
+ timestamp last_synced_at
+ timestamp created_at
+ }
+
+ relationships ||--o| relationship_types : has_type
+ relationships ||--o| relationship_statuses : has_status
```
-## Pipeline MediatR
+### Enumeration Values
-```
-Request → LoggingBehavior → ValidatorBehavior → TransactionBehavior → Handler → Response
- │ │ │
- ▼ ▼ ▼
- Log start/end Validate Begin/Commit
- + timing với Transaction
- FluentValidation
-```
+**RelationshipType:**
+| ID | Name |
+|----|------|
+| 1 | Friendship |
+| 2 | Following |
-### Thứ Tự Behaviors
+**RelationshipStatus:**
+| ID | Name |
+|----|------|
+| 1 | Pending |
+| 2 | Accepted |
+| 3 | Rejected |
+| 4 | Cancelled |
-1. **LoggingBehavior** - Ghi log xử lý request với timing
-2. **ValidatorBehavior** - Validate request sử dụng FluentValidation
-3. **TransactionBehavior** - Bao bọc command handlers trong database transactions
+## Domain Events Flow
-## Xử Lý Lỗi
-
-### Phân Cấp Exceptions
-
-```
-Exception
-└── DomainException
- └── SampleDomainException
-```
-
-### Problem Details (RFC 7807)
-
-Tất cả lỗi được trả về theo định dạng Problem Details:
-
-```json
-{
- "type": "https://tools.ietf.org/html/rfc7807",
- "title": "Lỗi Validation",
- "status": 400,
- "detail": "Một hoặc nhiều lỗi validation đã xảy ra.",
- "errors": {
- "Name": ["Tên là bắt buộc"]
- }
-}
+```mermaid
+graph LR
+ subgraph "Relationship Aggregate"
+ R[Relationship] -->|Creates| E1[FriendRequestSentEvent]
+ R -->|Accept| E2[FriendshipCreatedEvent]
+ R -->|Follow| E3[UserFollowedEvent]
+ R -->|Any Change| E4[StatusChangedEvent]
+ R -->|Remove| E5[RemovedEvent]
+ end
+
+ subgraph "UserBlock Aggregate"
+ UB[UserBlock] -->|Creates| E6[UserBlockedEvent]
+ end
+
+ subgraph "Event Handlers"
+ E1 --> H1[Notification Handler]
+ E2 --> H2[Notification Handler]
+ E3 --> H3[Feed Handler]
+ E6 --> H4[Cleanup Handler]
+ end
+
+ style R fill:#50c878,stroke:#2d8659,color:#fff
+ style UB fill:#50c878,stroke:#2d8659,color:#fff
```
## Health Checks
@@ -250,18 +300,38 @@ spec:
## Cân Nhắc Bảo Mật
-1. **Authentication**: JWT Bearer token (cấu hình trong production)
-2. **Authorization**: Role-based access control
+1. **Authentication**: JWT Bearer token từ IAM Service
+2. **Authorization**: Kiểm tra ownership trong business logic
3. **Input Validation**: FluentValidation trên tất cả requests
-4. **SQL Injection**: EF Core parameterized queries
-5. **Secrets**: Biến môi trường, không bao giờ trong code
+4. **Block Enforcement**: Block một user sẽ ngăn mọi tương tác
+5. **SQL Injection**: EF Core parameterized queries
+
+## Tích Hợp Với Các Services Khác
+
+```mermaid
+graph LR
+ SS[Social Service] <-->|Integration Events| EB[Event Bus]
+ EB <--> IAM[IAM Service]
+ EB <--> NS[Notification Service]
+ EB <--> FS[Feed Service]
+
+ IAM -->|UserCreated/Updated| SS
+ SS -->|FriendshipCreated| NS
+ SS -->|UserFollowed| FS
+
+ style SS fill:#4a90d9,stroke:#2d5986,color:#fff
+ style EB fill:#9b59b6,stroke:#7d3c98,color:#fff
+```
## Tối Ưu Hiệu Năng
1. **Connection Pooling**: EF Core với Npgsql connection resilience
2. **Async/Await**: Tất cả I/O operations đều async
-3. **Response Caching**: Thêm caching headers cho queries
-4. **Database Indexes**: Cấu hình trong EntityConfigurations
+3. **Pagination**: Tất cả list queries hỗ trợ skip/take
+4. **Indexes**:
+ - `relationships`: (requester_id, addressee_id, type_id)
+ - `user_blocks`: (blocker_id, blocked_id)
+ - `user_profiles`: (user_id) UNIQUE
## Tài Liệu Tham Khảo
diff --git a/services/social-service-net/docs/vi/README.md b/services/social-service-net/docs/vi/README.md
index e188f513..c428c92d 100644
--- a/services/social-service-net/docs/vi/README.md
+++ b/services/social-service-net/docs/vi/README.md
@@ -1,19 +1,25 @@
-# Template Microservice .NET 10
+# Social Service - Dịch Vụ Quản Lý Quan Hệ Xã Hội
-> Template microservice .NET 10 cấp doanh nghiệp theo các pattern DDD, CQRS và Clean Architecture.
+> Microservice .NET 10 quản lý quan hệ xã hội giữa users: kết bạn, theo dõi và block.
## Tổng Quan
-Template này cung cấp cấu trúc sẵn sàng production cho microservices .NET dựa trên kiến trúc tham chiếu eShopOnContainers với:
+Social Service là microservice quản lý tất cả các mối quan hệ xã hội giữa users trong GoodGo Platform:
-- **Domain-Driven Design (DDD)** - Aggregates, Entities, Value Objects, Domain Events
-- **CQRS Pattern** - Tách biệt Commands (ghi) và Queries (đọc) với MediatR
-- **Clean Architecture** - Phân tầng Domain, Infrastructure, API
-- **EF Core 10** - PostgreSQL với connection resilience
-- **FluentValidation** - Validation request
-- **API Versioning** - Versioning theo URL segment
-- **Health Checks** - Probes sẵn sàng cho Kubernetes
-- **Structured Logging** - Serilog với console và Seq
+- **Kết bạn (Friendship)** - Gửi/chấp nhận/từ chối yêu cầu kết bạn, hủy kết bạn
+- **Theo dõi (Following)** - Follow/unfollow users
+- **Block (User Blocking)** - Block/unblock users với lý do tùy chọn
+- **User Profile** - Đồng bộ thông tin user từ IAM Service
+
+### Kiến Trúc & Patterns
+
+| Pattern | Triển khai |
+|---------|-----------|
+| **DDD** | Aggregates, Entities, Value Objects, Domain Events |
+| **CQRS** | Commands/Queries tách biệt với MediatR |
+| **Clean Architecture** | 3-layer: Domain, Infrastructure, API |
+| **EF Core 10** | PostgreSQL với connection resilience |
+| **Structured Logging** | Serilog với console và Seq |
## Yêu Cầu
@@ -21,41 +27,11 @@ Template này cung cấp cấu trúc sẵn sàng production cho microservices .N
|---------|-----------|
| .NET SDK | 10.0.101+ |
| Docker | 24.0+ |
-| PostgreSQL | 15+ (hoặc dùng Docker) |
-
-```bash
-# Kiểm tra phiên bản .NET
-dotnet --version
-# Kết quả nên là: 10.0.xxx
-```
+| PostgreSQL | 15+ |
## Bắt Đầu Nhanh
-### 1. Tạo Service Mới
-
-```bash
-# Sao chép template sang service mới
-cp -r services/_template_dot_net services/your-service-name
-
-# Di chuyển đến thư mục service
-cd services/your-service-name
-
-# Đổi tên tất cả "SocialService" thành "YourService"
-find . -type f -name "*.cs" -exec sed -i '' 's/SocialService/YourService/g' {} +
-find . -type f -name "*.csproj" -exec sed -i '' 's/SocialService/YourService/g' {} +
-```
-
-### 2. Cấu Hình Môi Trường
-
-```bash
-# Sao chép template môi trường
-cp .env.example .env
-
-# Chỉnh sửa với cấu hình của bạn
-nano .env
-```
-
-### 3. Chạy với Docker
+### Chạy với Docker
```bash
# Khởi động tất cả services (API + PostgreSQL + Redis)
@@ -65,7 +41,7 @@ docker-compose up -d
docker-compose logs -f socialservice-api
```
-### 4. Chạy Local
+### Chạy Local
```bash
# Khôi phục dependencies
@@ -78,53 +54,101 @@ dotnet build
dotnet run --project src/SocialService.API
```
-## Cấu Trúc Dự Án
+## Domain Model
-```
-_template_dot_net/
-├── src/
-│ ├── SocialService.API/ # Lớp Presentation (Controllers, CQRS)
-│ │ ├── Controllers/ # Các API endpoints
-│ │ ├── Application/ # Triển khai CQRS
-│ │ │ ├── Commands/ # Thao tác ghi (MediatR)
-│ │ │ ├── Queries/ # Thao tác đọc
-│ │ │ ├── Behaviors/ # MediatR pipeline behaviors
-│ │ │ └── Validations/ # FluentValidation validators
-│ │ ├── Middleware/ # Custom middleware
-│ │ └── Program.cs # Điểm vào ứng dụng
-│ │
-│ ├── SocialService.Domain/ # Lớp Domain (Business logic thuần túy)
-│ │ ├── AggregatesModel/ # Aggregate roots và entities
-│ │ ├── Events/ # Domain events
-│ │ ├── Exceptions/ # Domain exceptions
-│ │ └── SeedWork/ # Base classes (Entity, ValueObject, etc.)
-│ │
-│ └── SocialService.Infrastructure/ # Lớp Infrastructure (Truy cập dữ liệu)
-│ ├── EntityConfigurations/ # Cấu hình EF Core Fluent API
-│ ├── Repositories/ # Triển khai repositories
-│ ├── Idempotency/ # Xử lý idempotency request
-│ └── SocialServiceContext.cs # DbContext với Unit of Work
-│
-├── tests/
-│ ├── SocialService.UnitTests/ # Unit tests (Domain, Application)
-│ └── SocialService.FunctionalTests/ # Integration tests (API endpoints)
-│
-├── Dockerfile # Multi-stage Docker build
-├── docker-compose.yml # Thiết lập phát triển local
-├── global.json # Pin phiên bản .NET SDK
-└── Directory.Build.props # Thuộc tính MSBuild chung
+### Aggregates
+
+#### 1. Relationship Aggregate
+Quản lý quan hệ kết bạn và theo dõi giữa users.
+
+```mermaid
+classDiagram
+ class Relationship {
+ +Guid Id
+ +Guid RequesterId
+ +Guid AddresseeId
+ +RelationshipType Type
+ +RelationshipStatus Status
+ +DateTime CreatedAt
+ +DateTime? UpdatedAt
+ +Accept()
+ +Reject()
+ +Cancel()
+ +Remove()
+ }
+
+ class RelationshipType {
+ <>
+ Friendship
+ Following
+ }
+
+ class RelationshipStatus {
+ <>
+ Pending
+ Accepted
+ Rejected
+ Cancelled
+ }
+
+ Relationship --> RelationshipType
+ Relationship --> RelationshipStatus
```
-## Các Endpoint API
+**Business Rules:**
+- Following tự động được chấp nhận
+- Friendship yêu cầu xác nhận từ người nhận
+- Không thể tạo quan hệ với chính mình
+- Chỉ Pending requests mới có thể Accept/Reject/Cancel
+- Chỉ Accepted relationships mới có thể Remove
+
+#### 2. UserBlock Aggregate
+Quản lý việc block users.
+
+| Field | Mô tả |
+|-------|-------|
+| `BlockerId` | ID user thực hiện block |
+| `BlockedId` | ID user bị block |
+| `Reason` | Lý do block (tùy chọn) |
+| `CreatedAt` | Thời gian block |
+
+#### 3. UserProfile Aggregate
+Cache thông tin user được đồng bộ từ IAM Service.
+
+| Field | Mô tả |
+|-------|-------|
+| `UserId` | ID user từ IAM |
+| `DisplayName` | Tên hiển thị |
+| `AvatarUrl` | URL avatar |
+| `Bio` | Tiểu sử |
+| `LastSyncedAt` | Thời gian sync cuối |
+
+## API Endpoints
+
+### Relationships - Kết Bạn
| Method | Endpoint | Mô Tả |
|--------|----------|-------|
-| `GET` | `/api/v1/samples` | Lấy tất cả samples |
-| `GET` | `/api/v1/samples/{id}` | Lấy sample theo ID |
-| `POST` | `/api/v1/samples` | Tạo sample mới |
-| `PUT` | `/api/v1/samples/{id}` | Cập nhật sample |
-| `DELETE` | `/api/v1/samples/{id}` | Xóa sample |
-| `PATCH` | `/api/v1/samples/{id}/status` | Thay đổi trạng thái |
+| `GET` | `/api/v1/relationships/users/{userId}/friends` | Lấy danh sách bạn bè |
+| `POST` | `/api/v1/relationships/friend-requests` | Gửi yêu cầu kết bạn |
+| `PUT` | `/api/v1/relationships/friend-requests/{id}` | Phản hồi yêu cầu (accept/reject) |
+| `GET` | `/api/v1/relationships/users/{id1}/mutual-friends/{id2}` | Lấy bạn chung |
+| `GET` | `/api/v1/relationships/users/{userId}/suggestions` | Gợi ý kết bạn |
+
+### Relationships - Theo Dõi
+
+| Method | Endpoint | Mô Tả |
+|--------|----------|-------|
+| `POST` | `/api/v1/relationships/follow` | Theo dõi user |
+| `DELETE` | `/api/v1/relationships/follow` | Bỏ theo dõi user |
+
+### Block Users
+
+| Method | Endpoint | Mô Tả |
+|--------|----------|-------|
+| `POST` | `/api/v1/blocks` | Block user |
+| `DELETE` | `/api/v1/blocks` | Unblock user |
+| `GET` | `/api/v1/blocks/users/{userId}` | Lấy danh sách blocked users |
### Health Endpoints
@@ -134,74 +158,79 @@ _template_dot_net/
| `/health/live` | Kiểm tra sống |
| `/health/ready` | Kiểm tra sẵn sàng |
-## Pattern CQRS
+## Cấu Trúc Dự Án
+
+```
+social-service-net/
+├── src/
+│ ├── SocialService.API/
+│ │ ├── Controllers/
+│ │ │ ├── RelationshipsController.cs # Friends + Following APIs
+│ │ │ └── BlocksController.cs # Block/Unblock APIs
+│ │ ├── Application/
+│ │ │ ├── Commands/ # 6 commands
+│ │ │ ├── Queries/ # 4 queries
+│ │ │ ├── Behaviors/ # MediatR pipeline
+│ │ │ └── Validations/ # FluentValidation
+│ │ └── Program.cs
+│ │
+│ ├── SocialService.Domain/
+│ │ ├── AggregatesModel/
+│ │ │ ├── RelationshipAggregate/ # Relationship, Status, Type
+│ │ │ ├── UserBlockAggregate/ # UserBlock
+│ │ │ └── UserProfileAggregate/ # UserProfile (IAM sync)
+│ │ ├── Events/ # 7 domain events
+│ │ ├── Exceptions/ # SocialDomainException
+│ │ └── SeedWork/ # Base classes
+│ │
+│ └── SocialService.Infrastructure/
+│ ├── EntityConfigurations/ # EF Core mappings
+│ ├── Repositories/ # 3 repositories
+│ ├── Idempotency/ # Request deduplication
+│ └── SocialServiceContext.cs # DbContext
+│
+├── tests/
+│ ├── SocialService.UnitTests/
+│ └── SocialService.FunctionalTests/
+│
+├── Dockerfile
+├── docker-compose.yml
+└── docs/
+```
+
+## CQRS Pattern
### Commands (Thao Tác Ghi)
-```csharp
-// Định nghĩa command
-public record CreateSampleCommand(string Name, string? Description)
- : IRequest;
-
-// Xử lý command
-public class CreateSampleCommandHandler : IRequestHandler
-{
- public async Task Handle(CreateSampleCommand request, CancellationToken ct)
- {
- var sample = new Sample(request.Name, request.Description);
- _repository.Add(sample);
- await _repository.UnitOfWork.SaveEntitiesAsync(ct);
- return new CreateSampleCommandResult(sample.Id);
- }
-}
-```
+| Command | Mô Tả |
+|---------|-------|
+| `SendFriendRequestCommand` | Gửi yêu cầu kết bạn |
+| `RespondToFriendRequestCommand` | Accept/Reject friend request |
+| `FollowUserCommand` | Theo dõi user |
+| `UnfollowUserCommand` | Bỏ theo dõi |
+| `BlockUserCommand` | Block user với lý do tùy chọn |
+| `UnblockUserCommand` | Unblock user |
### Queries (Thao Tác Đọc)
-```csharp
-// Định nghĩa query
-public record GetSampleQuery(Guid SampleId) : IRequest;
-```
+| Query | Mô Tả |
+|-------|-------|
+| `GetFriendsQuery` | Danh sách bạn bè với phân trang |
+| `GetMutualFriendsQuery` | Bạn chung giữa 2 users |
+| `GetFriendSuggestionsQuery` | Gợi ý kết bạn |
+| `GetBlockedUsersQuery` | Danh sách users bị block |
-## Domain Model
+## Domain Events
-### Aggregate Root
-
-```csharp
-public class Sample : Entity, IAggregateRoot
-{
- public string Name => _name;
- public SampleStatus Status => _status;
-
- public Sample(string name, string? description) {
- // Validation business logic
- if (string.IsNullOrWhiteSpace(name))
- throw new SampleDomainException("Tên sample không được để trống");
-
- // Domain event
- AddDomainEvent(new SampleCreatedDomainEvent(this));
- }
-
- public void Activate() {
- if (_status != SampleStatus.Draft)
- throw new SampleDomainException("Chỉ sample draft mới có thể kích hoạt");
- // Chuyển đổi trạng thái
- }
-}
-```
-
-## Kiểm Thử
-
-```bash
-# Chạy tất cả tests
-dotnet test
-
-# Chạy với coverage
-dotnet test /p:CollectCoverage=true /p:CoverageReportFormat=cobertura
-
-# Chạy project test cụ thể
-dotnet test tests/SocialService.UnitTests
-```
+| Event | Trigger |
+|-------|---------|
+| `FriendRequestSentDomainEvent` | Khi gửi friend request |
+| `FriendshipCreatedDomainEvent` | Khi friend request được accept |
+| `UserFollowedDomainEvent` | Khi follow user |
+| `RelationshipStatusChangedDomainEvent` | Khi status thay đổi |
+| `RelationshipRemovedDomainEvent` | Khi unfriend/unfollow |
+| `UserBlockedDomainEvent` | Khi block user |
+| `UserUnblockedDomainEvent` | Khi unblock user |
## Cấu Hình
@@ -210,9 +239,8 @@ dotnet test tests/SocialService.UnitTests
| Biến | Mô Tả | Mặc định |
|------|-------|----------|
| `ASPNETCORE_ENVIRONMENT` | Tên môi trường | `Development` |
-| `DATABASE_URL` | Connection string PostgreSQL | - |
-| `REDIS_URL` | Connection string Redis | - |
-| `JWT_SECRET` | Secret ký JWT (tối thiểu 32 ký tự) | - |
+| `DATABASE_URL` | PostgreSQL connection string | - |
+| `REDIS_URL` | Redis connection string | - |
### appsettings.json
@@ -227,6 +255,19 @@ dotnet test tests/SocialService.UnitTests
}
```
+## Kiểm Thử
+
+```bash
+# Chạy tất cả tests
+dotnet test
+
+# Chạy với coverage
+dotnet test /p:CollectCoverage=true
+
+# Chạy project test cụ thể
+dotnet test tests/SocialService.UnitTests
+```
+
## Triển Khai
### Docker Build
@@ -239,26 +280,11 @@ docker build -t socialservice:latest .
docker run -p 5000:8080 --env-file .env socialservice:latest
```
-### Kubernetes
-
-Xem [ARCHITECTURE.md](./ARCHITECTURE.md) để biết manifests triển khai Kubernetes.
-
-## Có Gì Mới Trong .NET 10
-
-- Tính năng ngôn ngữ **C# 14**
-- Hỗ trợ **Native AOT** được cải thiện
-- Hiệu suất **async/await** tốt hơn
-- **JSON serialization** được nâng cao
-- Cải thiện hiệu suất toàn diện
-- Hỗ trợ **LTS** 3 năm (đến tháng 11/2028)
-
## Tài Nguyên
+- [Tài liệu Kiến trúc](./ARCHITECTURE.md)
- [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) - Kiến trúc tham chiếu
-- [Tài liệu .NET 10](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10)
- [DDD với .NET](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/)
-- [MediatR](https://github.com/jbogard/MediatR) - Thư viện CQRS
-- [FluentValidation](https://docs.fluentvalidation.net/) - Thư viện validation
## Giấy Phép