diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/README.md b/packages/@aws-cdk/aws-applicationsignals-alpha/README.md
index a1791377be61f..952ea89d9aacd 100644
--- a/packages/@aws-cdk/aws-applicationsignals-alpha/README.md
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/README.md
@@ -287,3 +287,91 @@ class MyStack extends cdk.Stack {
}
}
```
+
+## Application Signals SLO L2 Constructs
+
+A collection of L2 constructs which leverages native L1 CFN resources, simplifying Application Signals Service Level
+Objectives (SLOs) creation process to monitor the reliability of a service against customer expectations.
+
+
+### ServiceLevelObjective
+
+`ServiceLevelObjective` aims to address key challenges in the current CDK process of creating and managing SLOs while providing the flexibility.
+The construct provides two types of SLOs:
+Period-based SLOs: Evaluate performance against goals using defined time periods.
+Request-based SLOs: Measure performance based on request success ratios
+
+Key Features:
+
+1. Easy creation of both period-based and request-based SLOs.
+2. Support for custom CloudWatch metrics and math expressions.
+3. Automatic error budget calculation and tracking.
+
+#### Use Case 1 - Create a Period-based SLO with custom metrics, default attainmentGoal: 99.9 and warningThreshold: 30
+
+```
+const periodSlo = ServiceLevelObjective.periodBased(this, 'PeriodSLO', {
+ name: 'my-period-slo',
+ goal: {
+ interval: Interval.rolling({
+ duration: 7,
+ unit: DurationUnit.DAY,
+ }),
+ },
+ metric: {
+ metricThreshold: 100,
+ periodSeconds: 300,
+ statistic: 'Average',
+ metricDataQueries: [/* ... */],
+ },
+});
+```
+
+#### Use Case 2 - Create a Period-based SLO with service/operation, attainmentGoal is 99.99 and warningThreshold is 50
+
+```
+const availabilitySlo = ServiceLevelObjective.periodBased(this, 'ApiAvailabilitySlo', {
+ name: 'api-availability-slo',
+ description: 'API endpoint availability SLO',
+ goal: {
+ attainmentGoal: 99.99,
+ warningThreshold: 50,
+ interval: Interval.calendar({
+ duration: 1,
+ unit: DurationUnit.MONTH,
+ // default startTime is now,
+ }),
+ },
+ metric: {
+ metricThreshold: 99,
+ metricType: MetricType.AVAILABILITY,
+ operationName: 'OrderProcessing',
+ keyAttributes: KeyAttributes.service({
+ name: 'MyService',
+ environment: 'Development',
+ });
+ periodSeconds: 300,
+ statistic: 'Average',
+ },
+});
+```
+
+#### Use Case 3 - Create request based SLO with custom metrics
+
+```
+const requestSlo = ServiceLevelObjective.requestBased(this, 'RequestSLO', {
+ name: 'my-request-slo',
+ goal: {
+ interval: Interval.calendar({
+ duration: 30,
+ unit: DurationUnit.DAY,
+ startTime: 1,
+ }),
+ },
+ metric: {
+ metricThreshold: 200,
+ goodCountMetrics: [/* ... */],
+ totalCountMetrics: [/* ... */],
+ },
+});
+```
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/index.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/index.ts
index a1a19a160235e..c39e5fdd288e5 100644
--- a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/index.ts
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/index.ts
@@ -4,3 +4,5 @@ export * from './enablement/constants';
export * from './enablement/instrumentation-versions';
export * from './enablement/ecs-cloudwatch-agent';
export * from './enablement/ecs-sdk-instrumentation';
+export * from './slo/slo';
+export * from './slo/slo-types';
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/constants.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/constants.ts
new file mode 100644
index 0000000000000..40957b29ae7d6
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/constants.ts
@@ -0,0 +1,162 @@
+/**
+ * Types of metrics that can be used for SLIs
+ */
+export enum MetricType {
+ /**
+ * Latency-based metric type
+ * Used for measuring response time or duration
+ */
+ LATENCY = 'LATENCY',
+
+ /**
+ * Availability-based metric type
+ * Used for measuring uptime or success rate
+ */
+ AVAILABILITY = 'AVAILABILITY'
+}
+
+/**
+ * Default values for goal configuration
+ */
+export const DEFAULT_GOAL_CONFIG = {
+ ATTAINMENT_GOAL: 99.9,
+ WARNING_THRESHOLD: 30,
+} as const;
+
+/**
+ * Comparison operators for metric thresholds
+ */
+export enum ComparisonOperator {
+ /**
+ * Greater than operator
+ * True if metric value is strictly greater than threshold
+ */
+ GREATER_THAN = 'GREATER_THAN',
+
+ /**
+ * Less than operator
+ * True if metric value is strictly less than threshold
+ */
+ LESS_THAN = 'LESS_THAN',
+
+ /**
+ * Greater than or equal operator
+ * True if metric value is greater than or equal to threshold
+ */
+ GREATER_THAN_OR_EQUAL = 'GREATER_THAN_OR_EQUAL',
+
+ /**
+ * Less than or equal operator
+ * True if metric value is less than or equal to threshold
+ */
+ LESS_THAN_OR_EQUAL = 'LESS_THAN_OR_EQUAL'
+}
+
+/**
+ * Statistical methods for aggregating metric values
+ */
+export enum MetricStatistic {
+ /**
+ * Average of all values in the period
+ */
+ AVERAGE = 'Average',
+
+ /**
+ * Sum of all values in the period
+ */
+ SUM = 'Sum',
+
+ /**
+ * Minimum value in the period
+ */
+ MINIMUM = 'Minimum',
+
+ /**
+ * Maximum value in the period
+ */
+ MAXIMUM = 'Maximum',
+
+ /**
+ * Count of samples in the period
+ */
+ SAMPLE_COUNT = 'SampleCount',
+
+ /**
+ * 99th percentile of values in the period
+ */
+ P99 = 'p99',
+
+ /**
+ * 95th percentile of values in the period
+ */
+ P95 = 'p95',
+
+ /**
+ * 90th percentile of values in the period
+ */
+ P90 = 'p90',
+
+ /**
+ * 50th percentile (median) of values in the period
+ */
+ P50 = 'p50'
+}
+
+/**
+ * Types of services that can be monitored
+ */
+export enum KeyAttributeType {
+ /**
+ * Service running in your account
+ */
+ SERVICE = 'SERVICE',
+
+ /**
+ * AWS managed service
+ */
+ AWS_SERVICE = 'AWS_SERVICE',
+
+ /**
+ * External service
+ */
+ REMOTE_SERVICE = 'REMOTE_SERVICE',
+
+ /**
+ * Resource
+ */
+
+ RESOURCE = 'RESOURCE',
+
+ /**
+ * AWS managed Resource
+ */
+
+ AWS_RESOURCE = 'AWS::RESOURCE'
+
+}
+
+/**
+ * Units for duration measurement in SLO intervals
+ */
+export enum DurationUnit {
+ /**
+ * Minute unit for fine-grained intervals
+ */
+ MINUTE = 'MINUTE',
+
+ /**
+ * Hour unit for medium-term intervals
+ */
+ HOUR = 'HOUR',
+
+ /**
+ * Day unit for daily intervals
+ */
+ DAY = 'DAY',
+
+ /**
+ * Month unit for long-term intervals
+ * Used for calendar-aligned monitoring
+ */
+ MONTH = 'MONTH'
+}
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/interval.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/interval.ts
new file mode 100644
index 0000000000000..c9f8f8bb36b85
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/interval.ts
@@ -0,0 +1,152 @@
+import { CalendarIntervalProps, GoalConfig, IInterval, IntervalProps } from './slo';
+import { DEFAULT_GOAL_CONFIG, DurationUnit } from './constants';
+
+/**
+ * Base class for Interval implementations
+ */
+export abstract class Interval implements IInterval {
+ /**
+ * Creates a calendar interval
+ */
+ public static calendar(props: CalendarIntervalProps): IInterval {
+ return new CalendarInterval(props);
+ }
+
+ /**
+ * Creates a rolling interval
+ */
+ public static rolling(props: IntervalProps): IInterval {
+ return new RollingInterval(props);
+ }
+
+ abstract bind(): any;
+}
+
+/**
+ * Implementation of Calendar Interval
+ */
+export class CalendarInterval extends Interval {
+ /**
+ * The duration value for the interval
+ * Must be greater than 0
+ *
+ * @private
+ */
+ private readonly duration: number;
+
+ /**
+ * The unit of duration measurement
+ * Can be MINUTE, HOUR, DAY, or MONTH
+ *
+ * @private
+ */
+ private readonly unit: DurationUnit;
+
+ /**
+ * The start time of the interval
+ * Specified as Unix timestamp in milliseconds
+ * Default starts from now
+ *
+ * @private
+ */
+ private readonly startTime: number;
+
+ constructor(props: CalendarIntervalProps) {
+ super();
+ this.duration = props.duration;
+ this.unit = props.unit;
+ this.startTime = props.startTime?? Date.now();
+ this.validate();
+ }
+
+ private validate() {
+ if (this.duration <= 0) {
+ throw new Error('Duration must be greater than 0');
+ }
+
+ if (this.unit === DurationUnit.MONTH && this.duration > 12) {
+ throw new Error('Month duration cannot exceed 12');
+ }
+
+ if (this.unit === DurationUnit.DAY && this.duration > 31) {
+ throw new Error('Day duration cannot exceed 31');
+ }
+
+ if (this.startTime <= 0) {
+ throw new Error('Start time must be greater than 0');
+ }
+ }
+
+ bind() {
+ return {
+ calendarInterval: {
+ duration: this.duration,
+ durationUnit: this.unit,
+ startTime: this.startTime,
+ },
+ };
+ }
+}
+
+/**
+ * Implementation of Rolling Interval
+ */
+export class RollingInterval extends Interval {
+ private readonly duration: number;
+ private readonly unit: DurationUnit;
+
+ constructor(props: IntervalProps) {
+ super();
+ this.duration = props.duration;
+ this.unit = props.unit;
+ this.validate();
+ }
+
+ private validate() {
+ if (this.duration <= 0) {
+ throw new Error('Duration must be greater than 0');
+ }
+
+ if (this.unit === DurationUnit.MONTH && this.duration > 12) {
+ throw new Error('Month duration cannot exceed 12');
+ }
+
+ if (this.unit === DurationUnit.DAY && this.duration > 31) {
+ throw new Error('Day duration cannot exceed 31');
+ }
+ }
+
+ bind() {
+ return {
+ rollingInterval: {
+ duration: this.duration,
+ durationUnit: this.unit,
+ },
+ };
+ }
+}
+
+/**
+ * Implementation of goal configuration
+ */
+export class Goal {
+ /**
+ * Creates a new goal configuration
+ */
+ public static of(props: GoalConfig): Goal {
+ return new Goal(props);
+ }
+
+ private constructor(private readonly props: GoalConfig) {}
+
+ /**
+ * Binds the goal configuration to L1 construct properties
+ */
+ public _bind(): applicationsignals.CfnServiceLevelObjective.GoalProperty {
+ return {
+ attainmentGoal: this.props.attainmentGoal ?? DEFAULT_GOAL_CONFIG.ATTAINMENT_GOAL,
+ warningThreshold: this.props.warningThreshold ?? DEFAULT_GOAL_CONFIG.WARNING_THRESHOLD,
+ interval: this.props.interval._bind(),
+ };
+ }
+}
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/keyAttributes.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/keyAttributes.ts
new file mode 100644
index 0000000000000..4c456e88dcd88
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/keyAttributes.ts
@@ -0,0 +1,104 @@
+import { KeyAttributeType } from './constants';
+import { KeyAttributesProps } from './slo';
+
+/**
+ * Class representing Application Signals key attributes with validation
+ */
+export class KeyAttributes {
+ private readonly props: KeyAttributesProps;
+
+ constructor(props: KeyAttributesProps) {
+ this.validateProps(props);
+ this.props = props;
+ }
+
+ private validateProps(props: KeyAttributesProps) {
+ if (!props.type) {
+ throw new Error('Type is required for Application Signals service');
+ }
+
+ if ([KeyAttributeType.SERVICE, KeyAttributeType.REMOTE_SERVICE, KeyAttributeType.AWS_SERVICE].includes(props.type)) {
+ if (!props.name || !props.environment) {
+ throw new Error('Name and Environment are required for Service types');
+ }
+ }
+
+ if ([KeyAttributeType.RESOURCE, KeyAttributeType.AWS_RESOURCE].includes(props.type)) {
+ if (props.name) {
+ throw new Error('Name is not allowed for Resource types');
+ }
+ if (props.resourceType === undefined) {
+ throw new Error('ResourceType is required for Resource types');
+ }
+ }
+ }
+
+
+ public bind(): { [key: string]: string } {
+ const attributes: { [key: string]: string } = {
+ Type: this.props.type,
+ Name: this.props.name,
+ Environment: this.props.environment,
+ };
+
+ if (this.props.resourceType) {
+ attributes.ResourceType = this.props.resourceType;
+ }
+
+ if (this.props.identifier) {
+ attributes.Identifier = this.props.identifier;
+ }
+
+ return attributes;
+ }
+
+ /**
+ * Creates key attributes for an AWS service
+ */
+ public static awsService(props: Omit): KeyAttributes {
+ return new KeyAttributes({
+ type: KeyAttributeType.AWS_SERVICE,
+ ...props,
+ });
+ }
+
+ /**
+ * Creates key attributes for a custom service
+ */
+ public static service(props: Omit): KeyAttributes {
+ return new KeyAttributes({
+ type: KeyAttributeType.SERVICE,
+ ...props,
+ });
+ }
+
+ /**
+ * Creates key attributes for a remote service
+ */
+ public static remoteService(props: Omit): KeyAttributes {
+ return new KeyAttributes({
+ type: KeyAttributeType.REMOTE_SERVICE,
+ ...props,
+ });
+ }
+
+ /**
+ * Creates key attributes for a resource
+ */
+ public static resource(props: Omit): KeyAttributes {
+ return new KeyAttributes({
+ type: KeyAttributeType.RESOURCE,
+ ...props,
+ });
+ }
+
+ /**
+ * Creates key attributes for an AWS resource
+ */
+ public static awsResource(props: Omit): KeyAttributes {
+ return new KeyAttributes({
+ type: KeyAttributeType.AWS_RESOURCE,
+ ...props,
+ });
+ }
+}
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/metric.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/metric.ts
new file mode 100644
index 0000000000000..0d5ab3f1a0f62
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/metric.ts
@@ -0,0 +1,357 @@
+import { ComparisonOperator, MetricStatistic, MetricType } from './constants';
+import { KeyAttributes } from './keyAttributes';
+
+/**
+ * Interface for metric dimension
+ */
+export interface MetricDimension {
+ /**
+ * Name of the dimension
+ *
+ * @required
+ */
+ readonly name: string;
+
+ /**
+ * Value of the dimension
+ *
+ * @required
+ */
+ readonly value: string;
+}
+
+/**
+ * Properties for Application Signals metrics
+ */
+export interface ApplicationSignalsMetricProps {
+ /**
+ * Type of the metric
+ *
+ * @required
+ */
+ readonly metricType: MetricType;
+
+ /**
+ * Key attributes for the service
+ *
+ * @required
+ */
+ readonly keyAttributes: KeyAttributes;
+
+ /**
+ * Operation name
+ *
+ * @default - undefined
+ */
+ readonly operationName?: string;
+
+ /**
+ * Period in seconds
+ * Required for period-based SLOs
+ *
+ * @required
+ */
+ readonly periodSeconds: number;
+
+ /**
+ * Statistic to use
+ * Required for period-based SLOs
+ *
+ * @required
+ */
+ readonly statistic: string;
+}
+
+/**
+ * Properties for CloudWatch metrics
+ */
+export interface CloudWatchMetricProps {
+ /**
+ * Metric data queries
+ *
+ * @required
+ */
+ readonly metricDataQueries: MetricDataQuery[];
+
+ /**
+ * Period in seconds
+ * Required for period-based SLOs
+ *
+ * @required
+ */
+ readonly periodSeconds: number;
+
+ /**
+ * Statistic to use
+ * Required for period-based SLOs
+ *
+ * @required
+ */
+ readonly statistic: string;
+}
+
+/**
+ * Properties for SLI metric configuration
+ */
+export type SliMetricBaseProps = {
+ /**
+ * The threshold value for the metric
+ *
+ * @required
+ */
+ readonly metricThreshold: number;
+
+ /**
+ * The comparison operator
+ *
+ * @default - Based on metric type
+ */
+ readonly comparisonOperator?: ComparisonOperator;
+} & (ApplicationSignalsMetricProps | CloudWatchMetricProps);
+
+
+/**
+ * Properties for a metric statistic
+ */
+export interface MetricStat {
+ /**
+ * The metric to query
+ *
+ * @required
+ */
+ readonly metric: MetricDefinition;
+
+ /**
+ * The period in seconds
+ * Must be a multiple of 60
+ *
+ * @default 60
+ */
+ readonly period: number;
+
+ /**
+ * The statistic to use
+ *
+ * @required
+ */
+ readonly stat: string;
+
+ /**
+ * The unit of the metric
+ *
+ * @default - no unit
+ */
+ readonly unit?: string;
+}
+
+/**
+ * Interface for metric definition
+ */
+export interface MetricDefinition {
+ /**
+ * Name of the metric
+ *
+ * @required
+ */
+ readonly metricName: string;
+
+ /**
+ * Namespace of the metric
+ *
+ * @required
+ */
+ readonly namespace: string;
+
+ /**
+ * Optional dimensions for the metric
+ *
+ * @default - no dimensions
+ */
+ readonly dimensions?: MetricDimension[];
+}
+
+/**
+ * Properties for a CloudWatch metric data query
+ * Used to define how metrics should be queried and processed
+ */
+export interface MetricDataQuery {
+ /**
+ * Unique identifier for the query
+ * Used to reference this query in math expressions
+ * Must be unique within the set of queries
+ *
+ * @required
+ */
+ readonly id: string;
+
+ /**
+ * AWS account ID where the metric is located
+ *
+ * @default - current account
+ */
+ readonly accountId?: string;
+
+ /**
+ * The math expression
+ * Cannot be specified if metricStat is specified
+ *
+ * @default - undefined
+ */
+ readonly expression?: string;
+
+ /**
+ * The metric statistic configuration
+ * Cannot be specified if expression is specified
+ *
+ * @default - undefined
+ */
+ readonly metricStat?: MetricStat;
+
+ /**
+ * Whether this query should return data to be used in results
+ * Set to true for queries that produce final values
+ * Set to false for intermediate calculations
+ *
+ * @default false
+ */
+ readonly returnData?: boolean;
+}
+
+/**
+ * Period-based metric properties with Application Signals
+ */
+export interface PeriodBasedAppSignalsMetricProps extends SliMetricBaseProps {
+ /**
+ * The type of metric being measured
+ * Can be LATENCY or AVAILABILITY
+ *
+ * @required
+ */
+ readonly metricType: MetricType;
+
+ /**
+ * Key attributes for the service being monitored
+ * Must include at least one of Type, Name, and Environment
+ *
+ * @required
+ */
+ readonly keyAttributes: { [key: string]: string };
+
+ /**
+ * The name of the operation being measured
+ * Used to filter metrics for specific operation
+ *
+ */
+ readonly operationName?: string;
+
+ /**
+ * The period in seconds for metric aggregation
+ * Must be a multiple of 60
+ *
+ * @required
+ */
+ readonly periodSeconds: number;
+
+ /**
+ * The statistic to use for aggregation
+ * Examples: Average, Sum, p99
+ *
+ * @required
+ */
+ readonly statistic: MetricStatistic;
+}
+
+/**
+ * Period-based metric properties with CloudWatch metrics
+ */
+export interface PeriodBasedCloudWatchMetricProps extends SliMetricBaseProps {
+ /**
+ * The metric data queries to execute
+ * Can include raw metrics and math expressions
+ *
+ * @required
+ */
+ readonly metricDataQueries: MetricDataQuery[];
+
+ /**
+ * The period in seconds for metric aggregation
+ * Must be a multiple of 60
+ *
+ * @required
+ */
+ readonly periodSeconds: number;
+
+ /**
+ * The statistic to use for aggregation
+ * Examples: Average, Sum, p99
+ *
+ * @required
+ */
+ readonly statistic: MetricStatistic;
+}
+
+/**
+ * Request-based metric properties with Application Signals
+ */
+export interface RequestBasedAppSignalsMetricProps extends SliMetricBaseProps {
+ /**
+ * The type of metric being measured
+ * Can be LATENCY or AVAILABILITY
+ *
+ * @required
+ */
+ readonly metricType: MetricType;
+
+ /**
+ * Key attributes for the applications being monitored
+ * Must include at least one of Type, Name, and Environment
+ *
+ * @required
+ */
+ readonly keyAttributes: { [key: string]: string };
+
+ /**
+ * The name of the operation being measured
+ *
+ */
+ readonly operationName?: string;
+}
+
+/**
+ * Request-based metric properties with CloudWatch metrics
+ */
+export interface RequestBasedCloudWatchMetricProps extends SliMetricBaseProps {
+ /**
+ * Metrics that count successful requests
+ * Optional if can be derived from total - bad
+ * Used to calculate success rate
+ *
+ * @required
+ */
+ readonly goodCountMetrics: MetricDataQuery[];
+
+ /**
+ * Metrics that count total requests
+ * Used as denominator for success rate
+ *
+ * @required
+ */
+ readonly totalCountMetrics: MetricDataQuery[];
+
+ /**
+ * Metrics that count failed requests
+ * Optional if can be derived from total - good
+ *
+ */
+ readonly badCountMetrics?: MetricDataQuery[];
+}
+
+/**
+ * Period-based metric properties
+ */
+export type PeriodBasedMetricProps = PeriodBasedAppSignalsMetricProps | PeriodBasedCloudWatchMetricProps;
+
+/**
+ * Request-based metric properties
+ */
+export type RequestBasedMetricProps = RequestBasedAppSignalsMetricProps | RequestBasedCloudWatchMetricProps;
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/slo-types.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/slo-types.ts
new file mode 100644
index 0000000000000..ddb728320d0be
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/slo-types.ts
@@ -0,0 +1,138 @@
+import { ISlo, PeriodBasedSloProps, RequestBasedSloProps } from './slo';
+import { ComparisonOperator, MetricType } from './constants';
+import { Goal } from './interval';
+import { PeriodBasedAppSignalsMetricProps, PeriodBasedCloudWatchMetricProps, RequestBasedAppSignalsMetricProps, RequestBasedCloudWatchMetricProps } from './metric';
+import { Resource } from 'aws-cdk-lib';
+import { Construct } from 'constructs';
+import * as applicationsignals from 'aws-cdk-lib/aws-applicationsginals';
+
+/**
+ * Base class for all SLO implementations
+ */
+export abstract class ServiceLevelObjective extends Resource implements ISlo {
+ public readonly arn: string;
+ public readonly name: string;
+ public readonly goal: Goal;
+
+ protected constructor(
+ scope: Construct,
+ id: string,
+ protected readonly props: PeriodBasedSloProps | RequestBasedSloProps
+ ) {
+ super(scope, id);
+ this.name = props.name;
+ this.goal = props.goal;
+ }
+
+ public static periodBased(
+ scope: Construct,
+ id: string,
+ props: PeriodBasedSloProps
+ ): ServiceLevelObjective {
+ return new PeriodBasedSlo(scope, id, props);
+ }
+
+ public static requestBased(
+ scope: Construct,
+ id: string,
+ props: RequestBasedSloProps
+ ): ServiceLevelObjective {
+ return new RequestBasedSlo(scope, id, props);
+ }
+
+ protected createBurnRateConfigurations(): applicationsignals.CfnServiceLevelObjective.BurnRateConfigurationProperty[] | undefined {
+ return this.props.burnRateWindows?.map(minutes => ({
+ lookBackWindowMinutes: minutes,
+ }));
+ }
+
+ protected getMetricComparisonOperator(metric: PeriodBasedAppSignalsMetricProps | PeriodBasedCloudWatchMetricProps | RequestBasedAppSignalsMetricProps | RequestBasedCloudWatchMetricProps): ComparisonOperator {
+ return metric.comparisonOperator ?? this.getDefaultComparisonOperator(metric.metricType);
+ }
+
+ protected getPeriod(periodSeconds?: number): number {
+ return periodSeconds ?? 60;
+ }
+
+ protected isAppSignalsMetric(metric: PeriodBasedAppSignalsMetricProps | PeriodBasedCloudWatchMetricProps | RequestBasedAppSignalsMetricProps | RequestBasedCloudWatchMetricProps): boolean {
+ return 'metricType' in metric && 'keyAttributes' in metric;
+ }
+
+ protected getDefaultComparisonOperator(metricType?: MetricType): ComparisonOperator {
+ switch (metricType) {
+ case MetricType.LATENCY:
+ return ComparisonOperator.LESS_THAN_OR_EQUAL;
+ case MetricType.AVAILABILITY:
+ return ComparisonOperator.GREATER_THAN_OR_EQUAL;
+ default:
+ throw new Error('ComparisonOperator must be specified when metricType is not provided');
+ }
+ }
+ abstract _bind(): applicationsignals.CfnServiceLevelObjectiveProps;
+}
+
+/**
+ * Period-based slo
+ */
+export class PeriodBasedSlo extends ServiceLevelObjective {
+ constructor(scope: Construct, id: string, props: PeriodBasedSloProps) {
+ super(scope, id, props);
+ }
+
+ _bind(): applicationsignals.CfnServiceLevelObjectiveProps {
+ const metric = this.props.metric;
+ return {
+ name: this.name,
+ description: this.props.description,
+ goal: this.goal._bind(),
+ burnRateConfigurations: this.createBurnRateConfigurations(),
+ sli: {
+ sliMetric: this.isAppSignalsMetric(metric) ? {
+ metricType: metric.metricType,
+ keyAttributes: metric.keyAttributes,
+ operationName: metric.operationName,
+ periodSeconds: this.getPeriod(metric.periodSeconds),
+ statistic: metric.statistic,
+ } : {
+ metricDataQueries: metric.metricDataQueries,
+ periodSeconds: this.getPeriod(metric.periodSeconds),
+ statistic: metric.statistic,
+ },
+ comparisonOperator: this.getMetricComparisonOperator(metric),
+ metricThreshold: metric.metricThreshold,
+ },
+ };
+ }
+}
+
+/**
+ * Request-based slo
+ */
+export class RequestBasedSlo extends ServiceLevelObjective {
+ constructor(scope: Construct, id: string, props: RequestBasedSloProps) {
+ super(scope, id, props);
+ }
+
+ _bind(): applicationsignals.CfnServiceLevelObjectiveProps {
+ const metric = this.props.metric;
+ return {
+ name: this.name,
+ description: this.props.description,
+ goal: this.goal._bind(),
+ burnRateConfigurations: this.createBurnRateConfigurations(),
+ requestBasedSli: {
+ requestBasedSliMetric: this.isAppSignalsMetric(metric) ? {
+ metricType: metric.metricType,
+ keyAttributes: metric.keyAttributes,
+ operationName: metric.operationName,
+ } : {
+ goodCountMetric: (metric as RequestBasedCloudWatchMetricProps).goodCountMetrics,
+ totalRequestCountMetric: (metric as RequestBasedCloudWatchMetricProps).totalCountMetrics,
+ badCountMetric: (metric as RequestBasedCloudWatchMetricProps).badCountMetrics,
+ },
+ comparisonOperator: this.getMetricComparisonOperator(metric),
+ metricThreshold: metric.metricThreshold,
+ },
+ };
+ }
+}
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/slo.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/slo.ts
new file mode 100644
index 0000000000000..76a5c6b91f9df
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/slo.ts
@@ -0,0 +1,224 @@
+import { DurationUnit, KeyAttributeType } from './constants';
+import { PeriodBasedMetricProps, RequestBasedMetricProps } from './metric';
+import { Goal } from './interval';
+
+/**
+ * Interface defining interval behavior
+ */
+export interface IInterval {
+ bind(): any;
+}
+
+/**
+ * Interface for interval properties
+ */
+export interface IntervalProps {
+ /**
+ * Duration value for the interval
+ * Must be greater than 0
+ *
+ * @required
+ */
+ readonly duration: number;
+
+ /**
+ * Unit of duration
+ * Can be MINUTE, HOUR, DAY, or MONTH
+ *
+ * @required
+ */
+ readonly unit: DurationUnit;
+}
+
+/**
+ * Interface for calendar interval properties
+ */
+export interface CalendarIntervalProps {
+ /**
+ * Duration value for the calendar interval
+ * Must be greater than 0
+ *
+ * @required
+ */
+ readonly duration: number;
+
+ /**
+ * Start time for the calendar interval
+ *
+ * @default - current timestamp
+ */
+ readonly startTime?: number;
+
+ /**
+ * Unit of duration
+ * Default is MONTH
+ *
+ */
+ readonly unit: DurationUnit;
+}
+
+/**
+ * Interface for goal configuration
+ */
+export interface GoalConfig {
+ /**
+ * The target goal percentage
+ * Must be between 0 and 100
+ *
+ * @default 99.9
+ */
+ readonly attainmentGoal?: number;
+
+ /**
+ * Warning threshold percentage
+ * Must be between 0 and 100
+ *
+ * @default 30
+ */
+ readonly warningThreshold?: number;
+
+ /**
+ * Interval configuration for the goal
+ *
+ * @required
+ */
+ readonly interval: IInterval;
+}
+
+/**
+ * Interface for key attributes
+ */
+export interface KeyAttributesProps {
+ /**
+ * The type of service
+ * Can be SERVICE, AWS_SERVICE, or REMOTE_SERVICE
+ *
+ * @required
+ */
+ readonly type: KeyAttributeType;
+
+ /**
+ * The name of the service
+ *
+ * @required
+ */
+ readonly name: string;
+
+ /**
+ * The environment of the service
+ *
+ * @required
+ */
+ readonly environment: string;
+
+ /**
+ * Optional additional identifier for the service
+ *
+ * @default - no identifier
+ */
+ readonly identifier?: string;
+
+ readonly resourceType?: string;
+
+}
+
+/**
+ * Interface defining SLO behavior and runtime properties
+ */
+export interface ISlo {
+ /**
+ * The ARN (Amazon Resource Name) of the SLO
+ * Generated by AWS when the SLO is created
+ * Used to reference this SLO in other AWS resources
+ *
+ * @required
+ */
+ readonly arn: string;
+
+ /**
+ * The name of the SLO
+ * Matches the name provided in the properties
+ *
+ * @required
+ */
+ readonly name: string;
+
+ /**
+ * The goal configuration of the SLO
+ * Contains the configured targets and time window
+ *
+ * @required
+ */
+ readonly goal: Goal;
+
+ /**
+ * Binds the SLO configuration to L1 construct properties
+ * Used internally by CDK to generate CloudFormation
+ *
+ * @returns The L1 construct properties
+ */
+ _bind(): any;
+}
+
+/**
+* Base interface for all SLO properties
+*/
+export interface SloBaseProps {
+ /**
+ * The name of the SLO
+ * Must be unique within the account/region
+ *
+ * @required
+ */
+ readonly name: string;
+
+ /**
+ * A description of the SLO's purpose
+ *
+ * @default - no description
+ */
+ readonly description?: string;
+
+ /**
+ * The goal configuration for the SLO
+ * Includes attainment target and time window
+ * Defines what "good" looks like for this SLO
+ *
+ * @required
+ */
+ readonly goal: Goal;
+
+ /**
+ * The burn rate windows in minutes
+ * Used to calculate error budget consumption
+ * Maximum of 10 windows
+ * Each window cannot exceed 7 days (10080 minutes)
+ *
+ * @default - no burn rate windows
+ */
+ readonly burnRateWindows?: number[];
+}
+
+/**
+ * Properties for period-based SLO configuration
+ */
+export interface PeriodBasedSloProps extends SloBaseProps {
+ /**
+ * The Period Based Slo metric configuration
+ *
+ * @required
+ */
+ readonly metric: PeriodBasedMetricProps;
+}
+
+/**
+ * Properties for request-based SLO configuration
+ */
+export interface RequestBasedSloProps extends SloBaseProps{
+ /**
+ * The Request Based Slo metric configuration
+ *
+ * @required
+ */
+ readonly metric: RequestBasedMetricProps;
+}
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/test/slo/interval.test.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/test/slo/interval.test.ts
new file mode 100644
index 0000000000000..d7a1c2352eeb2
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/test/slo/interval.test.ts
@@ -0,0 +1,134 @@
+import {Interval} from '../../lib/slo/interval'
+import {CalendarIntervalProps, IntervalProps} from '../../lib';
+import {DurationUnit} from "../../lib/slo/constants";
+
+describe('Interval', () => {
+ describe('calendar()', () => {
+ it('should create a calendar interval with valid props', () => {
+ const props: CalendarIntervalProps = {
+ unit: DurationUnit.MONTH,
+ duration: 1
+ };
+
+ const interval = Interval.calendar(props);
+ expect(interval).toBeDefined();
+ expect(interval.bind).toBeDefined();
+ });
+
+ it('should create a calendar interval with different time units', () => {
+ const units = [
+ DurationUnit.DAY,
+ DurationUnit.MONTH
+ ];
+
+ units.forEach(unit => {
+ const props: CalendarIntervalProps = {
+ unit: unit,
+ duration: 1
+ };
+ const interval = Interval.calendar(props);
+ expect(interval).toBeDefined();
+ });
+ });
+
+ it('should handle different duration values', () => {
+ const durations = [1, 7, 14, 30];
+
+ durations.forEach(duration => {
+ const props: CalendarIntervalProps = {
+ unit: DurationUnit.DAY,
+ duration
+ };
+ const interval = Interval.calendar(props);
+ expect(interval).toBeDefined();
+ });
+ });
+ });
+
+ describe('rolling()', () => {
+ it('should create a rolling interval with valid props', () => {
+ const props: IntervalProps = {
+ duration: 24,
+ unit: DurationUnit.HOUR
+ };
+
+ const interval = Interval.rolling(props);
+ expect(interval).toBeDefined();
+ expect(interval.bind).toBeDefined();
+ });
+
+ it('should handle different duration values', () => {
+ const durations = [1, 7, 14, 31];
+
+ durations.forEach(duration => {
+ const props: CalendarIntervalProps = {
+ unit: DurationUnit.DAY,
+ duration
+ };
+ const interval = Interval.calendar(props);
+ expect(interval).toBeDefined();
+ });
+ });
+ });
+
+ describe('bind()', () => {
+ it('should return the correct structure for calendar intervals', () => {
+ const props: CalendarIntervalProps = {
+ unit: DurationUnit.MONTH,
+ duration: 1
+ };
+
+ const interval = Interval.calendar(props);
+ const bound = interval.bind();
+ expect(bound).toBeDefined();
+
+ });
+
+ it('should return the correct structure for rolling intervals', () => {
+ const props: IntervalProps = {
+ duration: 24,
+ unit: DurationUnit.HOUR
+ };
+
+ const interval = Interval.rolling(props);
+ const bound = interval.bind();
+ expect(bound).toBeDefined();
+ });
+ });
+
+ describe('error cases', () => {
+ it('should handle invalid calendar interval props', () => {
+ expect(() => {
+ Interval.calendar({
+ unit: DurationUnit.DAY,
+ duration: -1
+ }).bind()
+ }).toThrow();
+ });
+
+ it('should handle invalid rolling interval props', () => {
+ expect(() => {
+ Interval.rolling({
+ duration: -1,
+ unit: DurationUnit.HOUR
+ } as any).bind()
+ }).toThrow();
+ });
+
+ it('should handle missing required properties', () => {
+ expect(() => {
+ Interval.calendar({
+ unit: DurationUnit.DAY,
+ duration: 0
+ } as CalendarIntervalProps).bind();
+ }).toThrow('Duration must be greater than 0');
+
+ expect(() => {
+ Interval.rolling({
+ unit: DurationUnit.HOUR,
+ duration: 0
+ } as IntervalProps).bind();
+ }).toThrow('Duration must be greater than 0');
+ });
+ });
+});
diff --git a/packages/@aws-cdk/aws-applicationsignals-alpha/test/slo/keyAttributes.test.ts b/packages/@aws-cdk/aws-applicationsignals-alpha/test/slo/keyAttributes.test.ts
new file mode 100644
index 0000000000000..3d0eb1178a8f0
--- /dev/null
+++ b/packages/@aws-cdk/aws-applicationsignals-alpha/test/slo/keyAttributes.test.ts
@@ -0,0 +1,137 @@
+import { KeyAttributes } from '../../lib/slo/keyAttributes';
+import { KeyAttributeType } from '../../lib/slo/constants';
+import { KeyAttributesProps } from "../../lib";
+
+describe('KeyAttributes', () => {
+ describe('constructor validation', () => {
+ test('throws error when type is missing', () => {
+ expect(() => {
+ new KeyAttributes({} as KeyAttributesProps);
+ }).toThrow('Type is required for Application Signals service');
+ });
+
+ describe('Service type validation', () => {
+ const serviceTypes = [
+ KeyAttributeType.SERVICE,
+ KeyAttributeType.REMOTE_SERVICE,
+ KeyAttributeType.AWS_SERVICE,
+ ];
+
+ serviceTypes.forEach(type => {
+ test(`requires name and environment for ${type}`, () => {
+ expect(() => {
+ new KeyAttributes({
+ type,
+ name: undefined,
+ environment: undefined
+ } as any);
+ }).toThrow('Name and Environment are required for Service types');
+
+ expect(() => {
+ new KeyAttributes({
+ type,
+ name: 'test',
+ environment: undefined
+ } as any);
+ }).toThrow('Name and Environment are required for Service types');
+
+ expect(() => {
+ new KeyAttributes({
+ type,
+ name: undefined,
+ environment: 'prod'
+ } as any);
+ }).toThrow('Name and Environment are required for Service types');
+ });
+
+ test(`accepts valid configuration for ${type}`, () => {
+ const props: KeyAttributesProps = {
+ type,
+ name: 'test',
+ environment: 'prod'
+ };
+ expect(() => {
+ new KeyAttributes(props);
+ }).not.toThrow();
+ });
+ });
+ });
+
+ describe('Resource type validation', () => {
+ const resourceTypes = [
+ KeyAttributeType.RESOURCE,
+ KeyAttributeType.AWS_RESOURCE,
+ ];
+
+ resourceTypes.forEach(type => {
+ test(`validates ${type} requirements`, () => {
+ expect(() => {
+ new KeyAttributes({
+ type,
+ name: 'test',
+ resourceType: 'someResource',
+ environment: 'prod'
+ } as any);
+ }).toThrow('Name is not allowed for Resource types');
+
+ expect(() => {
+ new KeyAttributes({
+ type,
+ resourceType: undefined
+ } as any);
+ }).toThrow('ResourceType is required for Resource types');
+ });
+
+ test(`accepts valid configuration for ${type}`, () => {
+ const props = {
+ type,
+ resourceType: 'someResource'
+ } as KeyAttributesProps;
+ expect(() => {
+ new KeyAttributes(props);
+ }).not.toThrow();
+ });
+ });
+ });
+ });
+
+ describe('bind method', () => {
+ test('returns correct attributes for service type', () => {
+ const keyAttributes = new KeyAttributes({
+ type: KeyAttributeType.SERVICE,
+ name: 'testService',
+ environment: 'beta'
+ });
+ expect(keyAttributes.bind()).toEqual({
+ Type: KeyAttributeType.SERVICE,
+ Name: 'testService',
+ Environment: 'beta'
+ });
+ });
+
+ test('returns correct attributes for resource type', () => {
+ const keyAttributes = new KeyAttributes({
+ type: KeyAttributeType.RESOURCE,
+ resourceType: 'database'
+ } as KeyAttributesProps);
+ expect(keyAttributes.bind()).toEqual({
+ Type: KeyAttributeType.RESOURCE,
+ ResourceType: 'database'
+ });
+ });
+ });
+
+ describe('static factory methods', () => {
+ test('service creates correct configuration', () => {
+ const keyAttributes = KeyAttributes.service({
+ name: 'testService',
+ environment: 'production'
+ });
+ expect(keyAttributes.bind()).toEqual({
+ Type: KeyAttributeType.SERVICE,
+ Name: 'testService',
+ Environment: 'production'
+ });
+ });
+ });
+});