Data Models
Module: Lens Version: 1.0.0-RELEASE Last Updated: October 25, 2025
Overview​
Total DTOs: 165 data transfer objects Database Tables: 3 primary tables (Snowflake) + 5 collections (MongoDB) + 7 tables (MySQL)
DTO Categories​
1. Cost Summary DTOs (dto/AwsVsActualCost/)​
CostSummaryDTO​
public class CostSummaryDTO {
private String service;
private Double awsCost;
private Double actualCost;
private Double variance;
private Double variancePercent;
private Double savingsRealized;
}
CostDetailDTO​
public class CostDetailDTO {
private String resourceId;
private String instanceType;
private String region;
private Double hourlyRate;
private Integer hours;
private Double totalCost;
}
2. Billing Console DTOs (dto/billingconsole/)​
BillingConsoleDTO​
public class BillingConsoleDTO {
private Double monthToDateCost;
private Double projectedMonthEndCost;
private Double budget;
private Double budgetUtilizationPercent;
private Integer daysElapsed;
private Integer daysRemaining;
private `List<ServiceCostDTO>` topServices;
private AlertDTO alert;
}
3. Reservation DTOs (dto/reservation/)​
ReservationResponseDTO​
public class ReservationResponseDTO {
private Integer totalReservations;
private Double totalMonthlyCost;
private Double totalSavings;
private `List<ReservationDTO>` reservations;
}
ReservationDTO​
public class ReservationDTO {
private String reservationId;
private String instanceType;
private String service; // EC2, RDS, ElastiCache
private String region;
private String availabilityZone;
private Integer instanceCount;
private String offeringClass; // standard, convertible
private String offeringType; // No Upfront, Partial, All Upfront
private String term; // 1yr, 3yr
private LocalDate startDate;
private LocalDate endDate;
private Integer daysUntilExpiry;
private Double utilizationPercent;
private Double monthlySavings;
}
4. RI Utilization DTOs (dto/reservation/)​
RiUtilizationDTO​
public class RiUtilizationDTO {
private String reservationId;
private String instanceType;
private Integer riHoursPurchased;
private Integer riHoursUsed;
private Double utilizationPercent;
private Integer unusedHours;
private Double wastedCost;
private AlertDTO alert; // If utilization \<80%
}
5. Cost Breakup DTOs (dto/costbreakup/)​
Structure: Each AWS service category has dedicated DTOs
DatabaseCostBreakupDTO​
public class DatabaseCostBreakupDTO {
private Double totalCost;
private RdsCostDTO rds;
private DynamoDbCostDTO dynamodb;
private ElastiCacheCostDTO elasticache;
private RedshiftCostDTO redshift;
}
S3CostBreakupDTO​
public class S3CostBreakupDTO {
private Double totalCost;
private `List<StorageClassCostDTO>` storageByClass;
private RequestCostDTO requestCosts;
private DataTransferCostDTO dataTransferCosts;
private `List<BucketCostDTO>` topBuckets;
private LifecyclePolicyEffectivenessDTO lifecyclePolicyEffectiveness;
}
6. CUDOS DTOs (dto/cudos/)​
S3DashboardDTO​
public class S3DashboardDTO {
private Double totalCost;
private Long totalStorageGB;
private `List<StorageClassDTO>` storageByClass;
private RequestCostsDTO requestCosts;
private DataTransferCostsDTO dataTransferCosts;
private `List<BucketDTO>` topBuckets;
}
7. Recommendation DTOs (dto/recommendation/)​
RecommendationDTO​
public class RecommendationDTO {
private String id;
private String type; // EC2_RIGHTSIZING, RI_PURCHASE, IDLE_RESOURCES
private String category; // COMPUTE, STORAGE, DATABASE, COMMITMENT, WASTE
private String severity; // HIGH, MEDIUM, LOW
private String title;
private String description;
private `List<ResourceDTO>` resources;
private Double totalMonthlySavings;
private Double annualSavings;
private String implementationDifficulty; // EASY, MEDIUM, HARD
private String riskLevel; // LOW, MEDIUM, HIGH
}
8. Alert DTOs (dto/costalert/)​
CostAlertDTO​
public class CostAlertDTO {
private String alertId;
private String alertName;
private String alertType; // BUDGET_THRESHOLD, ANOMALY
private `List<ThresholdDTO>` thresholds;
private Double budget;
private Boolean enabled;
}
9. Generic Request DTO​
GenericRequestDTO​
public class GenericRequestDTO {
@NotNull(message = "Customer ID required")
private String customerId;
@NotNull(message = "Account ID required")
private String accountId;
@NotNull(message = "Start date required")
@PastOrPresent(message = "Start date cannot be future")
private LocalDate startDate;
@NotNull(message = "End date required")
private LocalDate endDate;
@AssertTrue(message = "Date range cannot exceed 365 days")
public boolean isValidDateRange() {
return ChronoUnit.DAYS.between(startDate, endDate) <= 365;
}
}
Database Schemas​
Snowflake Tables​
COST_DAILY​
CREATE TABLE COST_DAILY (
DATE DATE NOT NULL,
ACCOUNT_ID VARCHAR(20) NOT NULL,
SERVICE VARCHAR(100) NOT NULL,
REGION VARCHAR(50),
USAGE_TYPE VARCHAR(200),
COST NUMBER(18,2),
USAGE_QUANTITY NUMBER(18,6),
CURRENCY VARCHAR(3) DEFAULT 'USD',
TAGS VARIANT, -- JSON column for AWS tags
PRIMARY KEY (DATE, ACCOUNT_ID, SERVICE, REGION, USAGE_TYPE)
) CLUSTER BY (DATE);
COST_HOURLY​
CREATE TABLE COST_HOURLY (
TIMESTAMP TIMESTAMP_NTZ NOT NULL,
ACCOUNT_ID VARCHAR(20) NOT NULL,
SERVICE VARCHAR(100),
REGION VARCHAR(50),
RESOURCE_ID VARCHAR(500),
COST NUMBER(18,6),
USAGE_QUANTITY NUMBER(18,6),
PRIMARY KEY (TIMESTAMP, ACCOUNT_ID, RESOURCE_ID)
) CLUSTER BY (TIMESTAMP);
RI_UTILIZATION​
CREATE TABLE RI_UTILIZATION (
DATE DATE NOT NULL,
ACCOUNT_ID VARCHAR(20),
RESERVATION_ID VARCHAR(100),
INSTANCE_TYPE VARCHAR(50),
REGION VARCHAR(50),
RI_HOURS_PURCHASED NUMBER(18,2),
RI_HOURS_USED NUMBER(18,2),
UTILIZATION_PCT NUMBER(5,2),
UNUSED_COST NUMBER(18,2),
PRIMARY KEY (DATE, RESERVATION_ID)
) CLUSTER BY (DATE);
MongoDB Collections​
saved_reports​
{
"_id": ObjectId("..."),
"reportId": "RPT-2024-001",
"customerId": "CUST-123",
"reportName": "Monthly EC2 Costs",
"reportType": "COST_SUMMARY",
"filters": {
"startDate": "2024-01-01",
"endDate": "2024-01-31",
"accounts": ["123456789012"],
"services": ["EC2"]
},
"createdAt": ISODate("2024-02-01T10:30:00Z"),
"shared": false
}
Indexes:
db.saved_reports.createIndex({ "customerId": 1, "reportType": 1 });
db.saved_reports.createIndex({ "reportId": 1 }, { unique: true });
filter_metadata​
{
"_id": ObjectId("..."),
"customerId": "CUST-123",
"filterType": "SERVICE",
"values": ["EC2", "RDS", "S3"],
"lastUpdated": ISODate("2024-02-15T08:00:00Z"),
"ttl": ISODate("2024-02-15T09:00:00Z")
}
MySQL Tables​
accounts​
CREATE TABLE accounts (
account_id VARCHAR(20) PRIMARY KEY,
customer_id VARCHAR(36) NOT NULL,
account_name VARCHAR(255),
account_type ENUM('PAYER', 'LINKED'),
status ENUM('ACTIVE', 'SUSPENDED'),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_customer (customer_id)
) ENGINE=InnoDB;
billing_metadata​
CREATE TABLE billing_metadata (
billing_id VARCHAR(36) PRIMARY KEY,
customer_id VARCHAR(36) NOT NULL,
billing_month DATE NOT NULL,
total_cost DECIMAL(18,2),
invoice_generated BOOLEAN DEFAULT FALSE,
UNIQUE KEY unique_customer_month (customer_id, billing_month)
) ENGINE=InnoDB;
Data Mapping Examples​
Snowflake → DTO Mapping​
Example: Mapping COST_DAILY to CostSummaryDTO
public class AwsVsActualCostDaoImpl implements AwsVsActualCostDao {
private final RowMapper<CostSummaryDTO> costSummaryRowMapper = (rs, rowNum) -> {
CostSummaryDTO dto = new CostSummaryDTO();
dto.setService(rs.getString("SERVICE"));
dto.setAwsCost(rs.getDouble("AWS_COST"));
dto.setActualCost(rs.getDouble("ACTUAL_COST"));
dto.setVariance(rs.getDouble("VARIANCE"));
dto.setVariancePercent(rs.getDouble("VARIANCE_PERCENT"));
dto.setSavingsRealized(rs.getDouble("SAVINGS"));
return dto;
};
@Override
public `List<CostSummaryDTO>` queryCostSummary(GenericRequestDTO request) {
String sql = loadQuery("query.cost.summary");
return jdbcTemplate.query(sql, costSummaryRowMapper,
request.getAccountId(), request.getStartDate(), request.getEndDate());
}
}
Validation Rules​
Field-Level Validation​
Example DTOs with JSR-303 validation:
public class ReportDTO {
@NotNull(message = "Customer ID required")
@Pattern(regexp = "CUST-[0-9]+", message = "Invalid customer ID format")
private String customerId;
@NotNull
@Size(min = 1, max = 10, message = "Select 1-10 accounts")
private `List<String>` accountIds;
@PastOrPresent(message = "Start date cannot be in future")
private LocalDate startDate;
@Future(message = "End date must be in future")
private LocalDate endDate;
@Min(value = 1, message = "Page must be >= 1")
private Integer page = 1;
@Min(1)
@Max(1000)
private Integer pageSize = 100;
}
Cross-Field Validation​
@AssertTrue(message = "End date must be after start date")
public boolean isValidDateRange() {
if (startDate == null || endDate == null) {
return true; // Let @NotNull handle null cases
}
return endDate.isAfter(startDate);
}
@AssertTrue(message = "Date range cannot exceed 365 days")
public boolean isWithinMaxRange() {
if (startDate == null || endDate == null) {
return true;
}
return ChronoUnit.DAYS.between(startDate, endDate) <= 365;
}
Summary​
DTOs: 165 total across 9 categories Snowflake Tables: 3 core tables (COST_DAILY, COST_HOURLY, RI_UTILIZATION) MongoDB Collections: 5 (saved_reports, filter_metadata, custom_queries, etc.) MySQL Tables: 7 (accounts, users, billing_metadata, etc.)
Validation: JSR-303 Bean Validation on all request DTOs Mapping: RowMapper for SQL → DTO conversion Serialization: Jackson for JSON serialization
Document Version: 1.0 Last Updated: October 25, 2025