An interactive, terminal-based operations and back-office Command-Line Interface (CLI) for managing customers, controlling operator sessions, and processing payment refunds in the GoPay ecosystem.
Built with Java 21, Spring Boot 4.1.1, and Spring Shell 4, featuring rich terminal UI components powered by JLine (custom colored prompt, masked password inputs, interactive component flows, bordered data tables, dynamic command availability, autocompletion), structured using Hexagonal Architecture (Ports and Adapters), and backed by a dedicated audit logging system.
- Features
- Architecture
- Tech Stack
- Prerequisites
- Getting Started
- CLI Command Reference
- Logging & Audit Trail
- Configuration
- Testing
- Contact
- Authentication & Dynamic Session Management:
- Secure Password Prompt: Masked password entry powered by JLine (
CommandContext.inputReader().readPassword()). - Session State Tracking: Operator credentials validation (built-in
admin/adminaccount) with in-memory session persistence. - Dynamic Command Availability: Commands dynamically enable or disable using Spring Shell
AvailabilityProvider.loginis disabled if already logged in;logoutis disabled if not logged in.
- Secure Password Prompt: Masked password entry powered by JLine (
- Customer Operations:
- Create: Add new customers with input validation (
@NotBlank,@Email) and status configuration (ACTIVEorINACTIVE, defaulting toACTIVE). - Find: Retrieve single customer records by ID with color-coded status output.
- List & Filter: Tabular rendering (
fancy_lightborders) of all customers or filtered by status (--status ACTIVE|INACTIVE). - Smart Shell Autocompletion: Auto-completion proposals (
Tab) for flags, status enum values, and sample inputs (createCustomerCompletionProvider,customerFilterCompletionProvider).
- Create: Add new customers with input validation (
- Payment & Refund Operations:
- Authentication Enforcement: Refunds require an active operator session (
Unauthenticated! You need to login first). - State-aware Validation: Verifies payment existence and ensures status is
SUCCESSbefore permitting a refund. - Interactive Terminal Prompts: Guides operators through an interactive
ComponentFlowto collect the refund reason and confirm the transaction before execution. - Dedicated Audit Trail: Successful refunds are logged to a segregated audit log (
logs/audit.log) capturing the operator username, payment ID, and reason.
- Authentication Enforcement: Refunds require an active operator session (
- Terminal UI & Experience:
- Custom Prompt: Colored prompt (
gopay-sh:>in green) defined viaPromptProvider. - Clean Terminal Output: Non-error application and framework logs are redirected to files; only errors surface in the console to keep the shell clutter-free.
- Application Banner: Custom ASCII art banner displaying the CLI version and Spring Boot runtime.
- Custom Prompt: Colored prompt (
- Hexagonal Architecture: Strict decoupling of domain logic, application use cases, persistence adapters, and CLI presentation.
- Containerized Database & Migrations: Pre-configured
docker-compose.ymlfor PostgreSQL 15 and automated Flyway schema migrations (V1__create_customer_table.sql,V2__add_payment_table.sql).
The project adheres to Hexagonal Architecture (Ports & Adapters / Clean Architecture):
flowchart TD
subgraph Driving_Adapters ["Driving Adapters - CLI Presentation"]
AC["AuthCommand (login, logout)"]
CC["CustomerCommand (create, find, list)"]
PC["PaymentCommand (refund)"]
CFG["Configs (Availability & Completion)"]
PP["GopayOperationsCliApplication (PromptProvider)"]
end
subgraph Application_Layer ["Application Services"]
CS["CustomerService"]
PS["PaymentService"]
end
subgraph Domain_Layer ["Domain Core"]
subgraph Customer_Domain ["Customer Domain"]
C["Customer Record"]
CR["<<interface>> CustomerRepository"]
CS_ENUM["Status: ACTIVE, INACTIVE"]
end
subgraph Payment_Domain ["Payment Domain"]
P["Payment Record"]
PR["<<interface>> PaymentRepository"]
PS_ENUM["PaymentStatus: SUCCESS, FAILED, REFUNDED"]
EX["Exceptions: InvalidPaymentState, PaymentNotFound"]
end
end
subgraph Driven_Adapters ["Driven Adapters - Persistence"]
CRA["CustomerRepositoryAdapter"]
PRA["PaymentRepositoryAdapter"]
CER["CustomerEntityRepository - Spring Data JPA"]
PER["PaymentEntityRepository - Spring Data JPA"]
CM["CustomerMapper - MapStruct"]
PM["PaymentMapper - MapStruct"]
end
subgraph Database ["Database & Audit"]
PG[("PostgreSQL 15")]
AUDIT["Audit Log (logs/audit.log)"]
end
Driving_Adapters --> Application_Layer
PC -.->|Checks Session| AC
PC -.->|Audit Events| AUDIT
Application_Layer --> Domain_Layer
Driven_Adapters -.->|Implements Ports| Domain_Layer
Application_Layer --> Driven_Adapters
Driven_Adapters --> Database
-
Domain Layer (
com.gofar.gopay.domain):- Pure business models and contracts with zero framework coupling.
- Immutable records:
Customer,Payment, andRefundDto. - Domain repository interfaces (Ports):
CustomerRepository,PaymentRepository. - Enums:
Status,PaymentStatus. - Domain business exceptions:
PaymentNotFoundException,InvalidPaymentStateException.
-
Application Layer (
com.gofar.gopay.application):- Orchestrates business use cases without exposing technical persistence details.
CustomerService: customer lookup, creation, listing all, and listing by status.PaymentService: refund eligibility validation and execution.
-
Infrastructure / Persistence Layer (
com.gofar.gopay.infrastructure.persistence):- Adapters implementing domain ports:
CustomerRepositoryAdapter,PaymentRepositoryAdapter. - JPA Entities:
CustomerEntity,PaymentEntity. - Spring Data JPA repositories:
CustomerEntityRepository,PaymentEntityRepository. - MapStruct mappers:
CustomerMapper,PaymentMapper.
- Adapters implementing domain ports:
-
CLI / Presentation Layer (
com.gofar.gopay.cli):AuthCommand: Handlesloginandlogout, masked password prompts, and tracks the current authenticated operator session.CustomerCommand: Customer operations with JLineTableBuilderformatting (fancy_lightborders) and status filtering.PaymentCommand: Interactive multi-stepComponentFlowfor refunds, session verification, and SLF4J audit logging.Configs: DeclaresAvailabilityProviderbeans (loginAvailabilityProvider,logoutAvailabilityProvider) andCompletionProviderbeans (createCustomerCompletionProvider,customerFilterCompletionProvider).GopayOperationsCliApplication: Configures the custom greengopay-sh:>prompt.
gopay-operations-cli/
├── docker/
│ ├── docker-compose.yml # PostgreSQL container setup
│ └── data/ # Mounted database volume (git ignored)
├── src/
│ ├── main/
│ │ ├── java/com/gofar/gopay/
│ │ │ ├── GopayOperationsCliApplication.java # Boot entry point & custom prompt provider
│ │ │ ├── application/ # Application services (use cases)
│ │ │ │ ├── customer/CustomerService.java
│ │ │ │ └── payment/PaymentService.java
│ │ │ ├── cli/ # Spring Shell presentation layer
│ │ │ │ ├── Configs.java # Availability & completion providers
│ │ │ │ ├── auth/AuthCommand.java # Login & logout commands
│ │ │ │ ├── customer/CustomerCommand.java # Customer management commands
│ │ │ │ └── payment/PaymentCommand.java # Refund command & audit integration
│ │ │ ├── domain/ # Pure business records, ports & enums
│ │ │ │ ├── customer/
│ │ │ │ │ ├── Customer.java
│ │ │ │ │ ├── CustomerRepository.java
│ │ │ │ │ └── Status.java
│ │ │ │ └── payment/
│ │ │ │ ├── InvalidPaymentStateException.java
│ │ │ │ ├── Payment.java
│ │ │ │ ├── PaymentNotFoundException.java
│ │ │ │ ├── PaymentRepository.java
│ │ │ │ ├── PaymentStatus.java
│ │ │ │ └── RefundDto.java
│ │ │ └── infrastructure/persistence/ # Persistence adapters & JPA entities
│ │ │ ├── customer/
│ │ │ │ ├── CustomerEntity.java
│ │ │ │ ├── CustomerEntityRepository.java
│ │ │ │ ├── CustomerMapper.java
│ │ │ │ └── CustomerRepositoryAdapter.java
│ │ │ └── payment/
│ │ │ ├── PaymentEntity.java
│ │ │ ├── PaymentEntityRepository.java
│ │ │ ├── PaymentMapper.java
│ │ │ └── PaymentRepositoryAdapter.java
│ │ └── resources/
│ │ ├── banner.txt # Custom ASCII art banner
│ │ ├── application.yaml # Core configuration & active profile
│ │ ├── application-dev.yaml # Development datasource configuration
│ │ ├── application-prod.yaml # Production datasource configuration
│ │ ├── logback-spring.xml # Segregated rolling logs & audit appender
│ │ └── db/migration/ # Flyway SQL migrations
│ │ ├── V1__create_customer_table.sql
│ │ └── V2__add_payment_table.sql
│ └── test/
│ └── java/com/gofar/gopay/
│ ├── application/PaymentServiceTest.java # Application service unit tests
│ └── cli/ # Shell integration tests
│ ├── AuthCommandTest.java
│ ├── CustomerCommandTest.java
│ └── PaymentCommandTest.java
├── pom.xml # Maven project descriptor
├── mvnw / mvnw.cmd # Maven wrapper scripts
└── README.md
| Component | Technology | Version |
|---|---|---|
| Language | Java | 21 |
| Framework | Spring Boot | 4.1.1 |
| CLI Framework | Spring Shell | 4.0.3 |
| Terminal UI | Spring Shell JLine / JLine 3 | 4.0.x |
| ORM & Data | Spring Data JPA / Hibernate | 6.x |
| Database | PostgreSQL | 15 |
| DB Migration | Flyway | PostgreSQL Edition |
| Code Generation | MapStruct & Lombok | 1.6.3 / 1.18.46 |
| Logging | Logback / SLF4J (rolling files & audit) | Included |
| Testing | JUnit 5, Mockito, AssertJ, Spring Shell Test | Included |
- Java Development Kit (JDK): Version 21 or higher installed and configured in
JAVA_HOME. - Docker & Docker Compose: Required for running the local PostgreSQL container.
- Maven: Version 3.9+ (or use the included
./mvnw/mvnw.cmdwrapper).
Use Docker Compose to spin up the local PostgreSQL 15 instance:
docker compose -f docker/docker-compose.yml up -dTo verify the container is running:
docker ps --filter "name=gopay_ctn"Build the application using the Maven wrapper:
Linux / macOS:
./mvnw clean packageWindows (PowerShell / Command Prompt):
.\mvnw.cmd clean packageLaunch the application:
Using Maven:
./mvnw spring-boot:runUsing the compiled JAR:
java -jar target/gopay-operations-cli-0.0.1-SNAPSHOT.jarOnce started, the application banner displays and the custom prompt appears:
.-'''-. _..._
' _ \ .-'_..._''. .---.
/ /` '. \_________ _...._ .' .' '.\| |.--.
.--./). | \ '\ |.' '-. .-. .- / .' | ||__|
/.''\\ | ' | '\ .'```'. '. \ \ / /. ' | |.--.
| | | |\ \ / / \ | \ \ __ \ \ / / | | | || |
\`-' / `. ` ..' / | | | |.:--.'. \ \ / / | | | || |
/("'` '-...-'` | \ / ./ | \ | \ \ / / . ' | || |
\ '---. | |\`'-.-' .' `" __ | | \ ` / \ '. .| || |
/'""'.\ | | '-....-'` .'.''| | \ / '. `._____.-'/| ||__|
|| || .' '. / / | |_ / / `-.______ / '---'
\'. __// '-----------' \ \._,\ '/`-' / `
`'---' `--' `" '..'
gopay-operations-cli 1.0
Powered by Spring Boot 4.1.1
gopay-sh:>
Type help to list all available commands.
Run commands directly from your terminal by passing arguments to the JAR:
java -jar target/gopay-operations-cli-0.0.1-SNAPSHOT.jar customer listSession authentication manages operator privileges. Certain sensitive actions (such as payment refunds) require authentication.
Authenticates the operator. Prompts for the password interactively with masking.
login -- <username>
- Default credentials: Username
admin/ Passwordadmin - Dynamic Availability: This command is only available when not logged in (
loginAvailabilityProvider).
Examples:
Successful login:
gopay-sh:> login -- admin
Password:
Welcome! Log in as admin successfully
Failed credentials:
gopay-sh:> login -- admin
Password:
Error! Invalid username or password
Already logged in:
gopay-sh:> login -- admin
Command 'login' is not currently available: You are already logged in
Terminates the active session and clears the authenticated operator state.
logout
- Dynamic Availability: This command is only available when an operator is currently logged in (
logoutAvailabilityProvider).
Examples:
Successful logout:
gopay-sh:> logout
Logout successfully
When not logged in:
gopay-sh:> logout
Command 'logout' is not currently available: You are not logged in
Registers a new customer. Requires name and email; status defaults to ACTIVE if omitted.
customer create --name <name> --email <email> [--status ACTIVE|INACTIVE]
Tip
Press Tab while typing this command to trigger createCustomerCompletionProvider autocompletion for --name, --email, and --status enum proposals (ACTIVE, INACTIVE).
Examples:
gopay-sh:> customer create --name "John Doe" --email "[email protected]"
Customer created: John Doe <[email protected]> [ACTIVE] ✓
gopay-sh:> customer create --name "Acme Corp" --email "[email protected]" --status INACTIVE
Customer created: Acme Corp <[email protected]> [INACTIVE] ✓
Retrieves details for a single customer by numeric ID.
customer find -- <id>
Examples:
gopay-sh:> customer find -- 1
Customer #1: John Doe (ACTIVE)
gopay-sh:> customer find -- 999
Customer not found: 999
Renders customers in an ASCII/ANSI formatted table with headers. Optionally filter results by status using --status.
customer list [--status ACTIVE|INACTIVE]
Tip
Press Tab after customer list --status to trigger customerFilterCompletionProvider autocompletion with ACTIVE and INACTIVE proposals.
List All Customers:
gopay-sh:> customer list
┌───┬───────────┬──────────────────────┬────────┐
│id │name │email │status │
├───┼───────────┼──────────────────────┼────────┤
│1 │John Doe │[email protected] │ACTIVE │
│2 │Acme Corp │[email protected] │INACTIVE│
└───┴───────────┴──────────────────────┴────────┘
Filter by Status:
gopay-sh:> customer list --status ACTIVE
┌───┬───────────┬──────────────────────┬────────┐
│id │name │email │status │
├───┼───────────┼──────────────────────┼────────┤
│1 │John Doe │[email protected] │ACTIVE │
└───┴───────────┴──────────────────────┴────────┘
Initiates an interactive refund process for a target payment ID.
payment refund -- <id>
Execution Flow & Business Rules:
- Authentication Check: Verifies that the operator is logged in. If unauthenticated:
Unauthenticated! You need to login first - Existence Validation: Verifies that the payment ID exists in the database. If not found:
Payment with id <id> not found - State Validation: Checks that the payment status is currently
SUCCESS. If it is alreadyREFUNDEDorFAILED:Payment <id> cannot be refunded. Current status: REFUNDED - Interactive ComponentFlow: Prompts the operator for details:
- Reason: Prompts operator to type a justification for the refund (
Reason:). - Confirmation: Prompts operator with
Confirm refund ? (Y/n):.
- Reason: Prompts operator to type a justification for the refund (
- Cancellation: If the operator declines confirmation:
The refund was cancelled - Execution & Audit Log: If confirmed:
- Updates payment status to
REFUNDEDin PostgreSQL. - Emits an audit entry in
logs/audit.logvia theAUDITlogger. - Displays confirmation:
Refund successful
- Updates payment status to
Interactive Example:
gopay-sh:> login -- admin
Password:
Welcome! Log in as admin successfully
gopay-sh:> payment refund -- 1
Reason: Customer requested cancellation
Confirm refund ? (Y/n): Y
Refund successful
| Command | Description |
|---|---|
help [command] |
Displays help menu or detailed documentation for a specific command |
history |
Inspects previously executed shell commands in current session |
clear or cls |
Clears the terminal screen |
quit or exit |
Exits the CLI shell session |
Tab |
Triggers intelligent command and argument autocompletion |
The application uses an advanced SLF4J / Logback configuration defined in src/main/resources/logback-spring.xml:
- Clean Console Output: A
ThresholdFilterrestricts console logs strictly toERRORlevel, preventing Spring Boot or Hibernate log noise from interfering with the interactive shell. - Application Log (
logs/gopay.log): All standard application events atINFOlevel and above are written to rolling files (logs/gopay.%d{yyyy-MM-dd}.gz, up to 30 days retention, 3GB cap). - Dedicated Audit Trail (
logs/audit.log): Sensitive administrative actions (such as payment refunds) write directly to theAUDITlogger withadditivity="false":12:30:15.123 [main] INFO AUDIT - User admin refunded payment with id 1. Reason: Customer requested cancellation
The project supports multi-profile setups via Spring Boot profiles:
| Profile | Configuration File | Purpose | Datasource Defaults |
|---|---|---|---|
dev (Default) |
application-dev.yaml |
Local developer environment | Default localhost:5432, DB ops_gopay_db, User tountoun |
prod |
application-prod.yaml |
Production deployment | Requires explicit environment variables |
To run with the production profile:
java -Dspring.profiles.active=prod -jar target/gopay-operations-cli-0.0.1-SNAPSHOT.jarThe application can be configured dynamically using environment variables:
| Variable | Description | dev Profile Default |
prod Profile Default |
|---|---|---|---|
HOST |
PostgreSQL server hostname | localhost |
(Required) |
PORT |
PostgreSQL server port | 5432 |
(Required) |
DB |
Target PostgreSQL database name | ops_gopay_db |
(Required) |
DB_USERNAME |
Database username | tountoun |
(Required) |
DB_PASSWORD |
Database password | tountoun |
(Required) |
Core Configuration (src/main/resources/application.yaml):
spring:
application:
name: gopay-operations-cli
version: 1.0
profiles:
active:
- dev
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
flyway:
enabled: true
logging:
file:
path: logsThe project contains unit and integration tests covering the domain logic, interactive Spring Shell commands, availability providers, and session authentication:
- Domain / Service Unit Tests:
PaymentServiceTest: Verifies payment lookups, refundable assertions, and status transitions.
- Shell Command Integration Tests:
AuthCommandTest: Uses Spring ShellShellTestClientandShellInputProviderto test valid/invalid logins, masked password input, and dynamic availability providers (loginAvailabilityProvider,logoutAvailabilityProvider).CustomerCommandTest: Verifies command routing, table generation, and data representation.PaymentCommandTest: Verifies authentication gating (Unauthenticated), payment not found handling, and refundable status checking.
./mvnw test./mvnw test -Dtest=AuthCommandTest
./mvnw test -Dtest=PaymentCommandTest
./mvnw test -Dtest=CustomerCommandTest
./mvnw test -Dtest=PaymentServiceTestFeel free to join me at [email protected]