Trip Offer Management System - Refactoring Documentation¶
Overview¶
The Trip Offer Management system has been refactored to follow the established TQPRO architectural patterns, providing proper separation of concerns and consistency with existing codebase standards.
Refactoring Summary¶
Before: Initial Implementation¶
- Direct use of
NTSEntityclasses in API layer - Business logic embedded in API
doXXXmethods - No canonical entity layer
- Direct EntityFacade usage without facade pattern
After: Refactored Implementation¶
- Proper layering: API → Facade → Canonical Entities → Native Entities → Database
- Business logic centralized in
TripOfferFacade - Clean separation of concerns
- Configuration-driven entity mapping
Architecture Layers¶
1. Database Layer¶
Location: tqapp/src/main/java/com/perun/tlinq/client/nts/db/trip/
Hibernate entity classes for PostgreSQL database tables:
- TrippageEntity - Maps to
nts.trip_pagetable - TripsnippetEntity - Maps to
nts.trip_snippettable - TripskeletonEntity - Maps to
nts.trip_skeletontable - TripsnippetfileEntity - Maps to
nts.trip_snippet_filetable
Features:
- JPA annotations (@Entity, @Table, @Column)
- Sequence-based ID generation
- Validation constraints
- Extends NTSEntity
2. Canonical Entity Layer¶
Location: tqapp/src/main/java/com/perun/tlinq/entity/trip/
Platform-independent entity classes:
- CTripPage - Canonical trip page entity
- CTripSnippet - Canonical trip snippet entity
- CTripSkeleton - Canonical skeleton template entity
- CTripSnippetFile - Canonical snippet file entity
Features:
- Extend TlinqEntity
- Implement Serializable
- Plain Java objects (POJOs)
- Business-focused field names
- No database coupling
3. Facade Layer¶
Location: tqapp/src/main/java/com/perun/tlinq/entity/trip/TripOfferFacade.java
Business logic layer following the facade pattern:
Methods:
// Page Operations
List<CTripPage> listPages(Integer page, Integer pageSize)
CTripPage readPage(Integer pageId)
CTripPage writePage(CTripPage page)
void deletePage(Integer pageId)
// Snippet Operations
List<CTripSnippet> listSnippets(Integer pageId, Integer page, Integer pageSize)
List<CTripSnippet> getActiveSnippets(Integer pageId)
CTripSnippet readSnippet(Integer snippetId)
CTripSnippet writeSnippet(CTripSnippet snippet)
void deleteSnippet(Integer snippetId)
// Template Operations
List<CTripSkeleton> listSkeletons()
CTripSkeleton readSkeleton(Integer skeletonId)
List<CTripSnippetFile> listSnippetFiles()
CTripSnippetFile readSnippetFile(Integer snippetFileId)
// HTML Generation
Map<String, Object> generateHtmlPage(Integer pageId, Integer skeletonId, Integer snippetFileId)
Features:
- Extends EntityFacade
- Uses NTSServiceFactory
- Validates input parameters
- Handles business logic
- Returns canonical entities
- Session management
4. API Layer¶
Location: tqapi/src/main/java/com/perun/tlinq/api/TripOfferApi.java
REST API endpoints - thin layer delegating to facade:
Pattern:
@POST
@Path("/endpoint")
public Response endpoint(Map reqData) {
try {
session = ApiUtil.gmp(reqData, "session", String.class, false);
ar = doEndpoint(reqData);
} catch (TlinqClientException e) {
ar = new TlinqApiResponse(e.getErrorCode(), e.getMessage());
}
return Response.ok(ar).build();
}
private TlinqApiResponse doEndpoint(Map reqData) throws TlinqClientException {
String session = ApiUtil.gmp(reqData, "session", String.class, false);
// Extract parameters
TripOfferFacade facade = new TripOfferFacade(session);
// Call facade method
return new TlinqApiResponse(result);
}
Features: - Parameter extraction only - Exception handling - Logging - Response wrapping - No business logic
5. Configuration Layer¶
Location: config/tourlinq-config.xml
Entity mappings defining canonical ↔ native transformations:
<Entity name="TripPage"
class="com.perun.tlinq.entity.trip.CTripPage"
idField="pageId"
defaultFactory="NTSServiceFactory">
<EntityFactoryList>
<Factory name="NTSServiceFactory"
nativeEntity="com.perun.tlinq.client.nts.db.trip.TrippageEntity">
<ServiceList>
<Service name="saveTripPage" action="create"/>
<Service name="saveTripPage" action="update"/>
<Service name="readTripPage" action="read"/>
<Service name="readTripPage" action="search"/>
<Service name="deleteTripPage" action="delete"/>
</ServiceList>
<FieldMappingList>
<FieldMapping targetField="pageId" sourceField="pageId" mapping="DirectMapping"/>
<FieldMapping targetField="pageName" sourceField="pageName" mapping="DirectMapping"/>
<FieldMapping targetField="pageDesc" sourceField="pageDesc" mapping="DirectMapping"/>
</FieldMappingList>
</Factory>
</EntityFactoryList>
</Entity>
Benefits of Refactoring¶
1. Separation of Concerns¶
- API Layer: HTTP handling and parameter extraction
- Facade Layer: Business logic and validation
- Entity Layer: Data representation
- Database Layer: Persistence
2. Maintainability¶
- Business logic centralized in facade
- Easy to locate and modify functionality
- Clear responsibilities per layer
3. Testability¶
- Facade can be unit tested independently
- Mock facade in API tests
- No HTTP dependencies in business logic
4. Consistency¶
- Follows existing patterns (GroupManagerFacade, etc.)
- Matches TQPRO architectural standards
- Uses established conventions
5. Flexibility¶
- Can swap database implementations
- Can change API structure without affecting business logic
- Configuration-driven mapping allows easy field changes
6. Reusability¶
- Facade methods can be called from multiple APIs
- Business logic not tied to REST endpoints
- Canonical entities usable across modules
Migration Path¶
If you need to add a new field:¶
-
Add to Database Entity:
-
Add to Canonical Entity:
-
Add Field Mapping:
-
Use in Facade (if needed):
If you need to add a new operation:¶
-
Add to Facade:
-
Add to API:
Comparison with Existing Patterns¶
The refactored Trip Offer Management system follows the same patterns as:
GroupManagerFacade Pattern¶
- Extends EntityFacade
- Uses NTSServiceFactory
- Search methods with SelectCriteriaList
- Type-safe canonical entities
Entity Mapping Pattern¶
- Similar to TripGroup, TripPax entities
- DirectMapping for simple fields
- Service definitions (create, update, read, search, delete)
- Native entity specified in factory
API Pattern¶
- Similar to GroupApi, VisaApi
- doXXX methods for business logic delegation
- Exception handling with TlinqApiResponse
- Session management
Performance Considerations¶
- Entity Transformation: Minimal overhead due to configuration-driven mapping
- Facade Layer: Single additional method call, negligible impact
- Caching: Can be added at facade level without API changes
- Pagination: Implemented in facade for memory efficiency
Security¶
- Session validation in facade constructor
- Parameter validation before database access
- TlinqClientException for controlled error messages
- No SQL injection risk (using JPA/Hibernate)
Future Enhancements¶
Potential Additions:¶
- Caching Layer: Add Hazelcast caching in facade
- Audit Trail: Add entity change tracking
- Validation: Add JSR-303 bean validation
- Batch Operations: Add bulk insert/update methods
- Search Filters: Add advanced search criteria
Extension Points:¶
- Add new factories in config (e.g., for different databases)
- Add computed fields with custom mappings
- Add entity lifecycle hooks
- Add field-level security
Conclusion¶
The refactored Trip Offer Management system now properly adheres to TQPRO architectural patterns:
✅ Layered Architecture: API → Facade → Entity → Database ✅ Separation of Concerns: Each layer has clear responsibility ✅ Configuration-Driven: Entity mapping in XML config ✅ Consistent Patterns: Matches existing codebase conventions ✅ Maintainable: Business logic centralized and testable ✅ Extensible: Easy to add fields, operations, or factories
The system maintains all original functionality while providing improved code organization, maintainability, and consistency with the established TQPRO architecture.
References¶
- GroupManagerFacade:
/tqapp/src/main/java/com/perun/tlinq/entity/group/GroupManagerFacade.java - TripGroup Entity Mapping:
/config/tourlinq-config.xml(line 2709) - GroupApi:
/tqapi/src/main/java/com/perun/tlinq/api/GroupApi.java - TQCOMMON Documentation:
/docs/tqcommon/TQCOMMON_EXECUTIVE_SUMMARY.md