Skip to main content

Linqra Sample Inventory Service

View the complete sample service implementation with CRUD operations and service-to-service communication.

Linqra Sample Product Service

View the sample product service implementation with REST endpoints for product management and integration with other microservices.

Creating Custom Microservices

This guide demonstrates how to create and configure new microservices that integrate with the Linqra platform. We’ll use the “Inventory Service” as a reference implementation.

Project Structure

A typical Linqra microservice follows this structure:

Creating a New Microservice

Let’s walk through creating the Inventory Service example:

1. Set Up Project Structure

Create the directories shown below:

2. Configure Maven Dependencies

Create a pom.xml with the necessary dependencies:

3. Create Main Application Class

Create the main application class InventoryServiceApplication.java:

4. Configure Service Discovery

Create EurekaClientConfig.java to enable service discovery:

5. Configure Application Properties

Create application.yml with necessary settings:

Run Configuration in IntelliJ

1. VM Options Configuration

Set the following VM options:
Inventory Service VM Options Configuration

2. Environment Variables

Set the following environment variables:
Inventory Service Environment Variables
Remember to adjust the paths according to your actual project location.

Verifying Service Registration

After starting your service, verify that it has registered with Eureka by accessing the Eureka dashboard at https://localhost:8761/. You should see your service listed in the “Instances currently registered with Eureka” section:
Inventory Service registered in Eureka
A successful registration means:
  • Your service shows as “UP” in the Status column
  • It appears with a unique instance ID
  • The API-GATEWAY service is also registered and running
If you see an emergency message about renewals, don’t be alarmed. This often appears during development when services are frequently started and stopped. As long as your service shows “UP”, it is properly registered.

Extending Your Microservice

Let’s extend our inventory service with additional components:

Updated Project Structure

After adding the controller and model classes, your project structure should look like this:

Adding Health Monitoring

1. Create Health Model

Create the HealthStatus.java class in the model package:

2. Create Health Controller

Create the HealthController.java class in the controller package:

Integration with API Gateway

The API Gateway will automatically communicate with your service’s health endpoint if health checking is enabled. The /api/health endpoint will return detailed information about your service’s health status, including:
  • Service identifier
  • Current status (UP/DOWN)
  • Service uptime
  • Current timestamp
  • Performance metrics like heap usage and CPU information
This allows the API Gateway to make intelligent routing decisions and implement circuit breaking if your service experiences issues.
Since we’re using dynamic port allocation (server.port: 0), you’ll need to check the Eureka dashboard or your service logs to determine the assigned port.

Best Practices for Controllers

When building REST APIs in your microservice:
  1. Use Proper Request Mapping: Prefix all endpoints with /api/{resource} for consistency
  2. Return Appropriate Status Codes:
    • 200 OK for successful operations
    • 201 Created for resource creation
    • 204 No Content for successful operations with no response body
    • 400 Bad Request for client errors
    • 404 Not Found when resources don’t exist
    • 500 Internal Server Error for server errors
  3. Validation: Add validation to request models using annotations like @Valid and constraint annotations
  4. Exception Handling: Create a global exception handler to provide consistent error responses
  5. Documentation: Use Swagger/OpenAPI annotations to document your API endpoints

Enhancing Security Configuration

Let’s extend our microservice with proper security configuration to validate JWT tokens and implement mutual TLS (mTLS) authentication.

Updated Project Structure

After adding the security components, your project structure should look like this:

Security Implementation

1. JWT Role Validation Filter

Create a filter to validate JWT tokens and check for required roles:

2. Security Configuration

Create the SecurityConfig.java class to configure Spring Security:

Understanding the Security Architecture

Dual Authentication Mechanism

Our microservice uses two authentication mechanisms:
  1. JWT Token Validation
    • OAuth2 Resource Server configuration authenticates JWT tokens from Keycloak
    • JwtRoleValidationFilter validates that tokens contain required roles:
      • Realm role: gateway_admin_realm
      • Client role: gateway_admin (for the linqra-gateway-client)
    • These roles were configured in Keycloak as described in the Keycloak Configuration documentation
  2. Mutual TLS (mTLS)
    • SSL configuration in application.yml enables client authentication (client-auth: want)
    • X509 configuration extracts the Common Name (CN) from client certificates
    • This enables secure service-to-service communication with certificate-based authentication

Authorization Flow

  1. When a request arrives, the JWT token is validated for proper signatures and expiration
  2. Our custom JwtRoleValidationFilter checks for the presence of required roles
  3. If mTLS is enabled, client certificates are also validated
  4. If all checks pass, the request is processed; otherwise, a 403 Forbidden response is returned

Testing Security Configuration

To test with a valid JWT token, you need to:
  1. Obtain a token from Keycloak using the client credentials grant type
  2. Include the token in the Authorization header of your requests:
The security configuration demands both proper JWT tokens and valid certificates. Make sure your API Gateway is correctly configured to pass these credentials to your microservice.

Creating REST API Controllers and Intercommunication

Before implementing business logic, let’s set up proper service-to-service communication and create our main API controller.

Updated Project Structure

After adding these components, your project structure will look like this:

Service Identification in Communication

1. Create Service Interceptor

The ServiceNameInterceptor adds a service identifier to all outgoing REST calls, which helps with logging, debugging, and request tracing:

2. Configure RestTemplate

Create a RestTemplateConfig class to set up a pre-configured RestTemplate with our interceptor:

3. Create Main Controller

Create a skeleton for the InventoryController that will house our business logic endpoints:

Understanding the Intercommunication Architecture

Service Identification

Every microservice in the Linqra ecosystem should identify itself in communications with other services. This provides several benefits:
  1. Request Tracing: The source of each request is clearly identified in logs
  2. Debugging: Makes troubleshooting complex service interactions easier
  3. Auditing: Allows for proper auditing of service-to-service communication
  4. Access Control: Enables service-specific access policies

RestTemplate Configuration

Our RestTemplateConfig provides:
  1. Media Type Support: Handles both JSON and binary data
  2. Service Identity: Automatically adds the service name to all outgoing requests
  3. Centralized Configuration: One place to add any future interceptors or converters

Building RESTful APIs

When implementing the InventoryController, follow these patterns for standard CRUD operations:

GET (Retrieve)

POST (Create)

DELETE (Remove)

Service-to-Service Communication Example

When your service needs to communicate with another microservice, use the injected RestTemplate:

Security Considerations for APIs

When implementing API endpoints, keep these security considerations in mind:
  1. Input Validation: Always validate incoming data with @Valid annotations
  2. Authentication Checks: Ensure endpoints check for appropriate authentication
  3. Authorization Logic: Implement fine-grained authorization in service methods
  4. Rate Limiting: Consider adding rate limiting for high-traffic endpoints
  5. Sensitive Data: Never expose sensitive data in responses

Implementing Business Logic with REST Controllers

Let’s finalize our Inventory Service by implementing a fully functional controller with CRUD operations and inter-service communication.

Updated Project Structure

After adding all the business logic components, your project structure will look like this:

Domain Models

First, let’s create the domain models for our inventory system:

1. InventoryItem

Create a model to represent inventory items:

2. ProductInfo

Create a model for product information that will be enriched with inventory data:

3. ProductAvailabilityResponse

Create a wrapper for product responses:

Complete Inventory Controller

Now let’s implement the full InventoryController with CRUD operations and service-to-service communication:

Understanding the Controller Implementation

CRUD Operations

The controller implements standard CRUD operations for inventory items:
  1. CREATE (POST): Adds a new inventory item
  2. READ (GET): Retrieves either all items or a specific item by ID
  3. UPDATE (PUT): Updates an existing inventory item
  4. DELETE (DELETE): Removes an inventory item

In-Memory Data Store

For simplicity, this implementation uses an in-memory HashMap to store inventory data:
  • inventoryItems: Map that stores items with their ID as the key
  • idCounter: Atomic counter that ensures unique IDs for new items
  • addMockItem: Helper method to initialize some sample data

Service-to-Service Communication

The /product-availability endpoint demonstrates service-to-service communication:
  1. It calls the Product Service (via the API Gateway) to get product information
  2. It enriches the product data with inventory information (availability, delivery estimates)
  3. It returns the combined data to the caller
This pattern showcases how microservices can collaborate to provide a richer API experience by combining their capabilities.

Testing the Inventory Service

When running the Inventory Service, you can test its endpoints:
  1. Get all items: GET https://localhost:{port}/inventory-service/api/inventory
  2. Get a specific item: GET https://localhost:{port}/inventory-service/api/inventory/1
  3. Create a new item: POST https://localhost:{port}/inventory-service/api/inventory
  4. Update an item: PUT https://localhost:{port}/inventory-service/api/inventory/1
  5. Delete an item: DELETE https://localhost:{port}/inventory-service/api/inventory/1
  6. Product availability: GET https://localhost:{port}/inventory-service/api/inventory/product-availability
Remember that the actual port will be dynamically assigned since we’re using server.port: 0. Check the Eureka dashboard or service logs to find the assigned port.
The inter-service communication will only work if the Product Service is also running and registered with Eureka. If it’s not running, the /product-availability endpoint will return an error.

Next Steps in Development

Now that you have implemented the core service with proper security, you can extend it with:
  1. Business Logic Controllers
    • Create additional controllers for your service’s functionality
    • Implement proper authorization checks based on user roles
  2. Database Integration
    • Add Spring Data repositories for persistence
    • Configure database connections in application.yml
  3. Service-to-Service Communication
    • Use RestTemplate or WebClient to call other microservices
    • Configure circuit breakers for resilience
  4. Testing
    • Implement unit tests for controllers and services
    • Create integration tests for full API verification
  5. Swagger Documentation
    • Add OpenAPI annotations to document endpoints
    • Configure Swagger UI for interactive documentation

Reference Implementation

A complete reference implementation of the Inventory Service is available on GitHub for your reference:

Linqra Sample Inventory Service

View the complete sample service implementation with CRUD operations and service-to-service communication.

Linqra Sample Product Service

View the sample product service implementation with REST endpoints for product management and integration with other microservices.
The repository includes:
  • Full implementation of the InventoryController with CRUD operations
  • Models for inventory items and product availability
  • Service-to-service communication with the Product Service
  • Mock data for testing purposes
This reference implementation demonstrates best practices for creating microservices that integrate with the Linqra platform and can serve as a starting point for your own services. By following this guide and referencing the sample implementation, you can create secure, cloud-native microservices that integrate seamlessly with the Linqra platform. Your services will be discoverable through Eureka, secured with both JWT tokens and mTLS, and ready for extension with your specific business logic.

CI/CD Deployment

The Inventory Service uses GitHub Actions for continuous integration and deployment. When code is merged into the master branch, it automatically deploys to EC2. Here’s the complete CI/CD configuration:

Automatic Deployment Process

When code is merged into the master branch, the following process occurs automatically:
  1. Source Code Upload
    • The entire source code is uploaded as an artifact
    • Docker compose file and pom.xml are uploaded separately
    • Kubernetes and keys configurations are uploaded if present
  2. EC2 Deployment
    • The deployment job is triggered only on master branch
    • SSH key is installed for secure EC2 access
    • Files are transferred to EC2 using rsync
    • Proper permissions are set for sensitive files
  3. Docker Operations
    • Unused Docker resources are pruned
    • Disk usage is checked
    • Docker network is ensured
    • Container is built and started
  4. Security
    • Sensitive files (keys) are handled with proper permissions
    • SSH key is used for secure deployment
    • Strict host key checking is disabled for automation

Required GitHub Secrets

The following secrets must be configured in your GitHub repository:
  • EC2_SSH_KEY_PROD: SSH private key for EC2 access
  • HOST_DNS_PROD: EC2 instance DNS
  • USERNAME_PROD: EC2 username (typically ‘ubuntu’)
  • TARGET_DIR_PROD: Target directory on EC2

Dependency Review

For pull requests, a dependency review is performed to check for:
  • High severity vulnerabilities
  • Outdated dependencies
  • License compliance
This ensures that your service remains secure and up-to-date with the latest dependencies.