Files
pos-system/services/membership-service-net/docs/en

Membership Service

Membership management service for GoodGo platform.

Overview

The Membership Service manages member information beyond basic IAM data. It handles:

  • Member Profiles - Membership information (country code, gender, preferences)
  • Membership Levels - Free, Basic, Premium tier management
  • Preferences - User preferences stored as JSON

Note

Profile information like avatar, phone, address, date of birth is managed by IAM Service's UserProfile. Membership Service only stores membership-specific data.

This service receives UserId from IAM Service and stores additional membership data using the same ID for consistency.

Prerequisites

Requirement Version
.NET SDK 10.0.101+
Docker 24.0+
PostgreSQL 15+ (Neon recommended)
# Check .NET version
dotnet --version
# Should output: 10.0.xxx

Quick Start

1. Configure Environment

# Copy environment template
cp .env.example .env

# Edit with your configuration
nano .env

2. Run with Docker Compose

# From deployments/local directory
cd deployments/local
docker-compose up -d membership-service-net

# View logs
docker-compose logs -f membership-service-net

3. Run Locally

# Navigate to service directory
cd services/membership-service-net

# Restore dependencies
dotnet restore

# Build all projects
dotnet build

# Run the API
dotnet run --project src/MembershipService.API

Project Structure

membership-service-net/
├── src/
│   ├── MembershipService.API/           # Presentation Layer
│   │   ├── Controllers/
│   │   │   └── MembersController.cs     # Member CRUD endpoints
│   │   ├── Application/
│   │   │   ├── Commands/                # CreateMember, UpdateProfile, ChangeLevel
│   │   │   ├── Queries/                 # GetMemberById, GetMembers
│   │   │   ├── Behaviors/               # Transaction, Validation, Logging
│   │   │   └── Validations/             # FluentValidation validators
│   │   └── Program.cs                   # App entry point
│   │
│   ├── MembershipService.Domain/        # Domain Layer
│   │   ├── AggregatesModel/
│   │   │   └── MemberAggregate/
│   │   │       ├── Member.cs            # Aggregate root
│   │   │       ├── MembershipLevel.cs   # Enumeration
│   │   │       └── IMemberRepository.cs # Repository interface
│   │   ├── Events/                      # Domain events
│   │   ├── Exceptions/                  # Domain exceptions
│   │   └── SeedWork/                    # Base classes
│   │
│   └── MembershipService.Infrastructure/
│       ├── EntityConfigurations/        # EF Core configs
│       ├── ExternalServices/            # IAM Service client
│       ├── Idempotency/                 # Request deduplication
│       ├── Repositories/                # Repository implementations
│       └── MembershipServiceContext.cs  # DbContext
│
├── tests/
│   ├── MembershipService.UnitTests/
│   └── MembershipService.FunctionalTests/
│
├── docs/
│   ├── en/                              # English documentation
│   └── vi/                              # Vietnamese documentation
│
├── Dockerfile
└── docker-compose.yml

API Endpoints

Member Management

Method Endpoint Description Auth Required
GET /api/v1/members Get paginated members (with search) Yes
GET /api/v1/members/{id} Get member by ID Yes
GET /api/v1/members/me Get current user's profile Yes
GET /api/v1/members/{id}/progress Get member's level progress Yes
GET /api/v1/members/{id}/experience Get member's EXP history Yes
POST /api/v1/members Create new member Yes
POST /api/v1/members/{id}/experience Add experience points Yes
PUT /api/v1/members/{id} Update member profile Yes

Health Endpoints

Endpoint Purpose
/health Full health status
/health/live Liveness probe (Kubernetes)
/health/ready Readiness probe (Kubernetes)

Swagger Documentation

Access Swagger UI at: http://localhost:5002/swagger

Domain Model

Member Aggregate

public class Member : Entity, IAggregateRoot
{
    public Guid UserId => Id;           // Same as IAM Service UserId
    public string CountryCode { get; }  // ISO 3166-1 alpha-2
    public string? Gender { get; }      // male, female, other
    public MembershipLevel MembershipLevel { get; }
    public string? Preferences { get; } // JSON
    public bool IsDeleted { get; }      // Soft delete
    public DateTime CreatedAt { get; }
    public DateTime UpdatedAt { get; }
    
    public void UpdateGender(string? gender);
    public void UpdateCountryCode(string countryCode);
    public void UpdatePreferences(string? preferencesJson);
    public void ChangeMembershipLevel(MembershipLevel newLevel);
    public void Delete();
    public void Restore();
}

Note

Profile fields like PhoneNumber, AvatarUrl, Address, DateOfBirth are managed by IAM Service's UserProfile and are not part of this Member entity.

Membership Levels

Level ID Description
Free 1 Default free tier
Basic 2 Basic paid membership
Premium 3 Premium membership

Domain Events

  • MemberCreatedDomainEvent - When new member is created
  • MemberUpdatedDomainEvent - When profile is updated
  • MembershipLevelChangedDomainEvent - When level changes

CQRS Pattern

Commands

// Create member
public class CreateMemberCommand : IRequest<CreateMemberResult>
{
    public Guid UserId { get; set; }
    public string CountryCode { get; set; } = "VN";
    public string? Gender { get; set; }
}

// Update profile
public class UpdateMemberProfileCommand : IRequest<UpdateMemberProfileResult>
{
    public Guid MemberId { get; set; }
    public string? Gender { get; set; }
    public string? CountryCode { get; set; }
    public string? Preferences { get; set; }
}

// Change membership level  
public class ChangeMembershipLevelCommand : IRequest<ChangeMembershipLevelResult>
{
    public Guid MemberId { get; set; }
    public int NewLevelId { get; set; }
}

Queries

// Get by ID
public class GetMemberByIdQuery : IRequest<MemberDto?>
{
    public Guid MemberId { get; set; }
}

// Get paginated list with search
public class GetMembersQuery : IRequest<GetMembersResult>
{
    public int PageIndex { get; set; } = 0;
    public int PageSize { get; set; } = 10;
    public string? SearchTerm { get; set; }
}

Testing

# Run all tests
dotnet test

# Run unit tests only
dotnet test tests/MembershipService.UnitTests

# Run functional tests only  
dotnet test tests/MembershipService.FunctionalTests

# Run with coverage
dotnet test /p:CollectCoverage=true

Test Summary:

  • Unit Tests: 12 tests (Domain Member + MembershipLevel)
  • Functional Tests: 3 tests (Controller authorization)

Configuration

Environment Variables

Variable Description Default
ASPNETCORE_ENVIRONMENT Environment name Development
DATABASE_URL PostgreSQL connection string -
Jwt__Authority JWT Authority URL -
Jwt__Audience JWT Audience -

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Database=membership;Username=postgres;Password=postgres"
  },
  "Jwt": {
    "Authority": "https://iam.goodgo.com",
    "Audience": "membership-api"
  },
  "IamService": {
    "BaseUrl": "http://iam-service-net:8080",
    "TimeoutSeconds": 30,
    "CacheDurationSeconds": 300
  }
}

Database

Run Migrations

cd services/membership-service-net

# Create new migration
dotnet ef migrations add InitialCreate -p src/MembershipService.Infrastructure -s src/MembershipService.API

# Apply migrations
dotnet ef database update -p src/MembershipService.Infrastructure -s src/MembershipService.API

Database Schema

-- Members table
CREATE TABLE members (
    id UUID PRIMARY KEY,
    country_code VARCHAR(2) NOT NULL DEFAULT 'VN',
    gender VARCHAR(10),
    membership_level_id INT NOT NULL DEFAULT 1,
    preferences JSONB,
    is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
    created_at TIMESTAMP NOT NULL,
    updated_at TIMESTAMP NOT NULL
);

-- Indexes
CREATE INDEX ix_members_membership_level ON members(membership_level_id);
CREATE INDEX ix_members_created_at ON members(created_at);
CREATE INDEX ix_members_is_deleted ON members(is_deleted);

Note

Fields like phone_number, avatar_url, address are not in this schema. They are stored in IAM Service's UserProfile.

Deployment

Docker Build

# Build Docker image
docker build -t membership-service:latest .

# Run container
docker run -p 5002:8080 --env-file .env membership-service:latest

Kubernetes

See ARCHITECTURE.md for Kubernetes deployment manifests.

  • IAM Service - Authentication, identity and UserProfile (iam-service-net)
  • Storage Service - File/avatar storage (storage-service-net)
  • Wallet Service - Payment and points (wallet-service-net)

IAM Service Integration

Membership Service communicates with IAM Service via IIamServiceClient:

public interface IIamServiceClient
{
    // User validation
    Task<IamUserInfo?> ValidateUserAsync(string accessToken);
    Task<IamUserInfo?> GetUserByIdAsync(string userId, string accessToken);
    Task<bool> UserExistsAsync(string userId, string accessToken);
    
    // Roles & Permissions
    Task<IReadOnlyList<string>> GetUserRolesAsync(string userId, string accessToken);
    Task<IReadOnlyList<string>> GetUserPermissionsAsync(string userId, string accessToken);
    Task<bool> HasPermissionAsync(string userId, string permission, string accessToken);
    Task<bool> HasRoleAsync(string userId, string role, string accessToken);
    
    // Health check
    Task<IamHealthStatus> CheckHealthAsync();
    Task<bool> IsAvailableAsync();
}

Resources

License

Proprietary - GoodGo Platform