Three Kid Family Application
A Spring Boot application that manages person records and identifies families matching specific criteria: couples with exactly three children where at least one child is under 18 years old.
Overview
This service processes person records in real-time, maintaining bidirectional relationship integrity between partners, parents, and children. After each record is received, it immediately determines if any stored person satisfies the "valid parent" pattern and returns all matching individuals.
Valid Parent Criteria
A person is considered a valid parent when they meet the following conditions:
- Has a partner - partnerId is not null
- Has exactly 3 children - childIds contains exactly 3 IDs
- Shared parenthood - All 3 children list both this person AND their partner as parents
- At least one minor - At least one child has a birthDate making them 18 years old or younger
Features
- Bidirectional relationship integrity - Relationships are automatically synchronized in both directions (if A lists B as a child, B automatically lists A as a parent)
- Real-time validation - Pattern matching occurs immediately after each upsert
- In-memory caching - Valid parents are cached for optimal performance
- Concurrent request handling - Method-level locking prevents race conditions
- PostgreSQL persistence - All data is stored in a PostgreSQL database
Architecture
Key Components
- PersonController - REST API endpoint handler
- PersonService - Core business logic with relationship management and caching
- PersonRepository - JPA repository for database operations
- Person - Domain entity representing an individual with relationships
Design Decisions
Method-level locking over optimistic locking: The service uses @Locked annotations rather than database-level optimistic locking to prevent transaction conflicts when relationship graphs overlap between concurrent requests. This provides better performance for workloads with high relationship overlap.
In-memory valid parent cache: Rather than querying the database for valid parents after each upsert, the service maintains a Set<Person> cache that is incrementally updated. This reduces database load.
Related persons map pattern: The retrieveRelatedOf() method fetches all partner/parent/child entities in a single query and passes them through the call chain, avoiding N+1 queries in most scenarios.
API Reference
POST /api/v1/people
Upsert a person record and receive all currently matching valid parents.
Request Body:
{
"id": 1,
"name": "John Doe",
"birthDate": "1992-03-14",
"partner": {"id": 2},
"parent1": {"id": 3},
"parent2": {"id": 4},
"children": [
{"id": 5},
{"id": 6},
{"id": 7}
]
}
Response:
- 200 OK - One or more valid parents exist. Returns array of all matching persons.
- 204 No Content - No valid parents currently match the criteria.
Note: The specification requests HTTP 444, but since this is a non-standard nginx status code, the implementation returns 204 No Content instead.
DELETE /api/v1/people (Phase 2 - Not Implemented)
Delete person records and blacklist their IDs permanently.
Request Body:
[1, 2, 3]
Development Instructions
Requirements
- Java 21+ (OpenJDK or Eclipse Temurin recommended)
- Maven 3.9+
- Docker & Docker Compose (for containerized deployment)
- PostgreSQL 15 (or use Docker Compose setup)
Local Development Setup
-
Clone the repository
git clone <repository-url> cd threekidfamily -
Configure database connection (if not using Docker)
Edit
application.propertiesor set environment variables:SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/mydb SPRING_DATASOURCE_USERNAME=myuser SPRING_DATASOURCE_PASSWORD=mypassword -
Build the application
mvn clean package -
Run tests
mvn test -
Run locally
mvn spring-boot:run
The application will be available at http://localhost:8080
Docker Deployment
The easiest way to run the application with its PostgreSQL database:
docker-compose up --build
The application will be available at http://localhost:8080
Performance Considerations
Optimizations
- Caching strategy: Valid parents are cached in-memory and incrementally updated
- Batch operations: Related persons are fetched in bulk to minimize database queries
- Defensive copies: Returned collection is deep-copied to prevent external mutations
Known Limitations
- Phase 2 not implemented: DELETE endpoint and blacklisting functionality is not yet available
- HTTP 444 substitution: Returns 204 instead of the specified HTTP 444 status code
- MAX_RELATED_ASSUMPTION: The service assumes no single person has more than 1000 related individuals (partners, parents, children). This prevents potential memory issues.
- Full table scan on startup:
populateCache()loads all persons into memory on startup. For very large datasets, consider implementing pagination or streaming.