Skip to content

PACK-Solutions/person

Repository files navigation

Person REST API

A RESTful API for managing person data, built with Kotlin, Ktor, Ktor native DI, Exposed, PostgreSQL, and Meilisearch.

Architecture

This project follows Domain-Driven Design (DDD) principles and Clean Architecture patterns:

  • Domain Layer: Contains the core business logic, entities, and interfaces.
  • Application Layer: Orchestrates the flow of data and coordinates the domain layer.
  • Infrastructure Layer: Provides implementations for interfaces defined in the domain layer.
  • Interface Layer: Handles HTTP requests and responses.

Features

  • CRUD operations for Person entities
  • Address management
  • Domain events for tracking changes

Prerequisites

  • JDK 21 or higher
  • PostgreSQL 17.5
  • Meilisearch 1.14.0

Running the Application

Environment Setup

  1. Copy the environment template file:

    cp .env.sample .env
    
  2. Edit the .env file and update the values with your actual API keys:

    • MEILISEARCH_API_KEY: Your Meilisearch API key
    • MEILI_MASTER_KEY: Your Meilisearch master key
    • DD_API_KEY: Your Datadog API key
    • DD_ENV: The deployment environment for Datadog (e.g., local, dev, prod)
    • DD_VERSION: The application version reported to Datadog (e.g., 0.0.1)

    Note: The .env file is ignored by git to keep your sensitive keys secure. Use local overrides or secret managers for production environments.

Using Docker Compose (Recommended)

  1. Start the entire application stack using docker-compose:

    docker-compose up -d
    

    This will start:

    • The Person API application on port 8080
    • PostgreSQL 17.5 on port 5432 with database name "person", username "postgres", and password "postgres"
    • Meilisearch 1.14.0 on port 7700 with your configured master key
    • Meilisearch UI on port 3000
  2. Alternatively, you can build and run just the application locally:

    ./gradlew run
    

    Note: If you're running the application locally while using Docker for the dependencies, you'll need to configure the application to use the correct connection details for PostgreSQL and Meilisearch.

Environment Variables

The application configuration can be overridden using the following environment variables:

  • PORT: The port on which the Ktor server will run (default: 8080)
  • DATABASE_URL: JDBC URL for the PostgreSQL database (default: jdbc:postgresql://localhost:5432/person)
  • DATABASE_USER: Username for the PostgreSQL database (default: postgres)
  • DATABASE_PASSWORD: Password for the PostgreSQL database (default: postgres)
  • MEILISEARCH_URL: URL for the Meilisearch server (default: http://localhost:7700)
  • MEILISEARCH_API_KEY: API key for the Meilisearch server (configured in .env file)
  • MEILI_MASTER_KEY: Master key for the Meilisearch server (configured in .env file)
  • DD_API_KEY: API key for Datadog monitoring (configured in .env file)
  • DD_ENV: Deployment environment tag for Datadog (e.g., local, dev, prod)
  • DD_VERSION: Application version tag for Datadog traces/logs (e.g., 0.0.1)

Using Individual Docker Containers

Alternatively, you can start the services individually:

  1. Start PostgreSQL:

    docker run -d --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=person postgres:17.5
    
  2. Start Meilisearch:

    docker run -d --name meilisearch -p 7700:7700 getmeili/meilisearch:v1.14.0
    
  3. Build and run the application:

    ./gradlew run
    

The API will be available at http://localhost:8080.

API Endpoints

Person Endpoints

  • GET /api/persons - Get all persons (both individuals and legal entities)
  • GET /api/persons/{id} - Get a person by ID
  • DELETE /api/persons/{id} - Delete a person

Individual Endpoints

  • POST /api/persons/individuals - Create a new individual
  • PUT /api/persons/individuals/{id} - Update an individual

Legal Entity Endpoints

  • POST /api/persons/legal-entities - Create a new legal entity
  • PUT /api/persons/legal-entities/{id} - Update a legal entity

Address Endpoints

  • POST /api/persons/{id}/addresses - Add an address to a person
  • DELETE /api/persons/{personId}/addresses - Remove an address from a person (using query parameters)

Data Model

The API supports two types of persons: Individuals and Legal Entities.

Person (Base)

Both Individual and Legal Entity types share the following common structure:

  • ID (UUID)
  • Type (INDIVIDUAL or LEGAL_ENTITY)
  • Addresses (list)

Individual

An individual person has the following specific fields:

  • Personal Info:

    • First Name
    • Last Name
    • Birth Date
    • Birth City
    • Birth Country
    • Nationality
    • Marital Status (enum: SINGLE, MARRIED, DIVORCED, WIDOWED, PACS, SEPARATED)
    • Guardianship Type (enum: NONE, GUARDIANSHIP, CURATORSHIP)
  • Additional Information:

    • Matrimonial Regime (enum: COMMUNITY_OF_PROPERTY, SEPARATION_OF_PROPERTY, COMMUNITY_OF_ACQUISITIONS, UNIVERSAL_COMMUNITY, PARTICIPATION_IN_ACQUISITIONS)
    • Social Security Number

Legal Entity

A legal entity has the following specific fields:

  • General Information:

    • Company Name
    • Commercial ID
    • Identifier
    • Mail Reference
    • SIREN
    • SIRET
    • Legal Form
    • Creation Date
  • Legal Information:

    • Commerce Register Number
    • Registration Date
    • Registration Country
    • Registration Place
    • Fiscal Year End Date
    • Employee Count
    • APE Code
    • Activity Sector
    • Share Capital
  • Legal Representative:

    • Name
    • Position
    • Email
    • Phone
    • Start Date
    • End Date (optional)

Address

Both person types can have multiple addresses with the following fields:

  • ID (UUID)
  • Type (enum: COMMUNICATION, HOME, WORK, OTHER)
  • Number
  • Street
  • City
  • Postal Code
  • Phone (optional)
  • Email (optional)
  • Is Secondary (boolean)
  • Is Undeliverable (boolean)
  • Validity Start Date (optional)
  • Validity End Date (optional)

Example Requests

Create an Individual

POST /api/persons/individuals
Content-Type: application/json

{
  "personalInfo": {
    "firstName": "John",
    "lastName": "Doe",
    "birthDate": "1980-01-15",
    "birthCity": "New York",
    "birthCountry": "United States",
    "nationality": "American",
    "maritalStatus": "MARRIED",
    "guardianshipType": "NONE"
  },
  "additionalInformation": {
    "matrimonialRegime": "COMMUNITY_OF_PROPERTY",
    "socialSecurityNumber": "123-45-6789"
  },
  "addresses": [
    {
      "type": "HOME",
      "number": "123",
      "street": "Main Street",
      "city": "Paris",
      "postalCode": "75001",
      "phone": "+33123456789",
      "email": "john.doe@example.com",
      "isSecondary": false,
      "isUndeliverable": false
    }
  ]
}

Create a Legal Entity

POST /api/persons/legal-entities
Content-Type: application/json

{
  "generalInformation": {
    "companyName": "Acme Corporation",
    "commercialId": "ACME2023",
    "identifier": "AC-12345",
    "mailReference": "ACME-MAIL-REF",
    "siren": "123456789",
    "siret": "12345678900012",
    "legalForm": "SAS",
    "creationDate": "2010-03-25"
  },
  "legalInformation": {
    "commerceRegisterNumber": "RCS PARIS B 123 456 789",
    "registrationDate": "2010-03-30",
    "registrationCountry": "France",
    "registrationPlace": "Paris",
    "fiscalYearEndDate": "12-31",
    "employeeCount": 42,
    "apeCode": "6201Z",
    "activitySector": "Computer programming activities",
    "shareCapital": "100000€"
  },
  "legalRepresentative": {
    "name": "Jane Smith",
    "position": "CEO",
    "email": "jane.smith@acme.com",
    "phone": "+33123456789",
    "startDate": "2015-01-01"
  },
  "addresses": [
    {
      "type": "COMMUNICATION",
      "number": "456",
      "street": "Business Avenue",
      "city": "Paris",
      "postalCode": "75008",
      "phone": "+33987654321",
      "email": "contact@acme.com",
      "isSecondary": false,
      "isUndeliverable": false
    }
  ]
}

Update an Individual

PUT /api/persons/individuals/{id}
Content-Type: application/json

{
  "personalInfo": {
    "firstName": "John",
    "lastName": "Doe",
    "birthDate": "1980-01-15",
    "birthCity": "New York",
    "birthCountry": "United States",
    "nationality": "American",
    "maritalStatus": "DIVORCED",
    "guardianshipType": "NONE"
  },
  "additionalInformation": {
    "matrimonialRegime": null,
    "socialSecurityNumber": "123-45-6789"
  }
}

Update a Legal Entity

PUT /api/persons/legal-entities/{id}
Content-Type: application/json

{
  "generalInformation": {
    "companyName": "Acme Corporation",
    "commercialId": "ACME2023-UPDATED",
    "identifier": "AC-12345",
    "mailReference": "ACME-MAIL-REF-NEW",
    "siren": "123456789",
    "siret": "12345678900012",
    "legalForm": "SAS",
    "creationDate": "2010-03-25"
  },
  "legalInformation": {
    "commerceRegisterNumber": "RCS PARIS B 123 456 789",
    "registrationDate": "2010-03-30",
    "registrationCountry": "France",
    "registrationPlace": "Paris",
    "fiscalYearEndDate": "12-31",
    "employeeCount": 50,
    "apeCode": "6201Z",
    "activitySector": "Computer programming activities",
    "shareCapital": "150000€"
  },
  "legalRepresentative": {
    "name": "Jane Smith",
    "position": "CEO",
    "email": "jane.smith@acme.com",
    "phone": "+33123456789",
    "startDate": "2015-01-01"
  }
}

Add an Address

POST /api/persons/{id}/addresses
Content-Type: application/json

{
  "type": "COMMUNICATION",
  "number": "456",
  "street": "Business Avenue",
  "city": "Paris",
  "postalCode": "75008",
  "phone": "+33987654321",
  "email": "contact@example.com",
  "isSecondary": false,
  "isUndeliverable": false,
  "validityStartDate": "2023-01-01T00:00:00Z"
}

Remove an Address

DELETE /api/persons/{personId}/addresses?type=HOME&streetNumber=123&streetName=Main%20Street&city=Paris&postalCode=75001

Conventional Commits and Releases (Cocogitto)

This repository uses Cocogitto (cog) to enforce Conventional Commits, automate versioning, and generate the changelog.

  • Why: consistent commit messages -> automated semantic versioning and clean release notes
  • Config: see cog.toml at the project root (changelog is written to CHANGELOG.md, CI skip token is [skip ci])

Install Cocogitto

Conventional Commit format

<type>(<optional scope>)!: <short summary>

Examples:

feat(person): add endpoint to create legal entities
fix(address): normalize postal code before persistence
docs: add API usage examples
refactor: extract address parsing utility
test: add IBAN validation tests
chore: upgrade dependencies
feat!: remove deprecated address fields (BREAKING)

Breaking changes can also be declared in the footer: BREAKING CHANGE: details about the breaking change

Validate commits locally

  • Check the history: cog check
  • Verify a specific commit message file (used by the hook): cog verify --file .git/COMMIT_EDITMSG

Git hook (commit-msg)

A commit-msg hook is defined in cog.toml and will:

  • verify the commit message format
  • run cog check to ensure the history stays valid

Install the hook:

  • Preferred: cog install-hook --all
  • If the above is not available in your version, create .git/hooks/commit-msg (chmod +x) with a script that runs:
cog verify --file "$1" && cog check

Versioning & Changelog

  • Auto bump based on commits since last tag: cog bump --auto
  • Force a bump: cog bump patch | minor | major
  • Generate/preview changelog: cog changelog
  • After a bump, a tag and CHANGELOG.md entry are generated according to cog.toml

CI

  • To skip CI for a commit, include [skip ci] in the commit message (configured in cog.toml).

About

Person API

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors