Skip to content

Bluetape4k AWS Part 2: Core Modules and Service Coverage

Small robotic operators wiring aws-java, aws-kotlin, CRT engine, and S3 TransferManager blocks on a Kotlin workbench
Creating an SDK client is easy. Keeping it clean at the service boundary is the harder part.

This is Part 2 of the bluetape4k-aws series. Part 1 covered the repository overview and mental model. This post focuses on the core modules: what bluetape4k-aws-java and bluetape4k-aws-kotlin actually remove from application code.

The goal is not to reimplement the AWS SDKs. The official AWS Java SDK v2 and AWS Kotlin SDK remain the foundation. bluetape4k-aws-java connects Java SDK v2 async clients to Kotlin coroutine code and makes the S3 CRT/TransferManager path easier to use for large files. bluetape4k-aws-kotlin keeps the native suspend model of the Kotlin SDK while adding CRT HTTP engine defaults, lifecycle helpers, and service-specific DSL builders.

Two AWS SDK approaches through bluetape4k aws-java and aws-kotlin toward AWS services
The two SDK approaches differ, but service code usually wants a small and consistent interface.

When a service uses AWS Java SDK v2 directly, there are several early decisions. Sync and async clients have different shapes. Async clients bring HTTP client selection and CompletableFuture composition. S3 transfer workloads add another layer because the best path for large files is often the CRT-backed S3 async client plus S3TransferManager.

bluetape4k-aws-java does not hide those choices. It prepares the repetitive wiring. The general async client factory currently defaults to Netty, while SdkAsyncHttpClientProvider.AwsCrt and awsCrtAsyncHttpClientOf provide a first-class CRT async HTTP client option. The S3 large-transfer path goes further: S3ClientFactory.CrtAsync.create uses S3AsyncClient.crtBuilder(), and S3ClientFactory.TransferManager.create builds a S3TransferManager on that CRT-backed async client.

The TransferManager factory also defaults its executor to Executors.newVirtualThreadPerTaskExecutor(). That makes the Java module a practical fit for high-throughput S3 transfer code where async completion, file I/O, and blocking edges meet.

import io.bluetape4k.aws.s3.S3ClientFactory
import io.bluetape4k.aws.s3.transfer.downloadFile
import io.bluetape4k.aws.s3.transfer.uploadFile
import software.amazon.awssdk.regions.Region
import java.nio.file.Path
suspend fun mirrorObject(bucket: String, key: String, source: Path, target: Path) {
val transferManager = S3ClientFactory.TransferManager.create(
region = Region.AP_NORTHEAST_2,
)
transferManager.uploadFile(bucket, key, source)
transferManager.downloadFile(bucket, key, target)
}

This does not replace TransferManager. AWS SDK provides that capability. bluetape4k-aws-java reduces the repeated assembly around it: CRT S3 async client creation, Virtual Thread executor selection, and async/coroutine extensions.

For smaller S3 objects, the coroutine helpers make the Java SDK v2 async client feel natural in Kotlin code. The SDK returns CompletableFuture; the extension functions await it behind a suspend API.

import io.bluetape4k.aws.s3.S3ClientFactory
import io.bluetape4k.aws.s3.getAsString
import io.bluetape4k.aws.s3.putAsString
suspend fun writeAndRead(bucket: String, key: String): String {
val client = S3ClientFactory.Async.create()
client.putAsString(bucket, key, "hello from bluetape4k-aws")
return client.getAsString(bucket, key)
}

The same idea appears across services. DynamoDB Enhanced Async Table scan/query can be consumed as Flow; SQS, SNS, SES, KMS, CloudWatch, CloudWatch Logs, Kinesis, and STS get async/coroutine helpers around the Java SDK v2 clients. The point is to keep Java SDK v2’s broad service coverage while making it less awkward in coroutine-heavy Kotlin services.

bluetape4k-aws-kotlin starts from a different place. AWS Kotlin SDK operations are already suspend functions. The useful work is not converting CompletableFuture; it is making client creation, HTTP engine selection, request builders, and lifecycle handling easier to repeat correctly.

The Kotlin module defaults its HTTP engine to CRT. HttpClientEngineProvider.defaultHttpEngine returns Crt.httpEngine, while HttpClientEngineProvider.OkHttp.httpEngine remains available when a service needs OkHttp explicitly. So the Kotlin SDK approach also has a CRT-first default with a clear escape hatch.

import aws.smithy.kotlin.runtime.net.url.Url
import io.bluetape4k.aws.kotlin.s3.putFromPath
import io.bluetape4k.aws.kotlin.s3.withS3Client
import java.nio.file.Path
suspend fun uploadReport(path: Path) {
withS3Client(
endpointUrl = Url.parse("http://localhost:4566"),
region = "ap-northeast-2",
) { client ->
client.putFromPath("reports", "daily/report.csv", path)
}
}

Helpers such as withS3Client, withSqsClient, and withDynamoDbClient create a client, run the block, and close it. Long-lived clients can use s3ClientOf, sqsClientOf, and similar factories directly. The module keeps one-shot and application-scoped client lifecycles separate.

The service helpers go beyond thin wrappers. S3 has file, byte-array, string, and stream helpers. SQS has queue-URL-oriented send, batch send, and receive helpers. DynamoDB has item-map helpers, batch execution, and request builders. Kinesis has recordFlow, a cold Flow<Record> that polls a single shard and handles iterator expiry plus retryable throttling behavior.

import io.bluetape4k.aws.kotlin.kinesis.KinesisStartingPosition
import io.bluetape4k.aws.kotlin.kinesis.recordFlow
suspend fun consumeShard(client: aws.sdk.kotlin.services.kinesis.KinesisClient) {
client.recordFlow(
streamName = "orders",
shardId = "shardId-000000000000",
position = KinesisStartingPosition.TrimHorizon,
).collect { record ->
println(record.data.decodeToString())
}
}

The core modules are useful because the pattern is repeated across many AWS services, not because there is one nice S3 helper. S3, SQS, SNS, DynamoDB, KMS, SES/SESv2, CloudWatch, CloudWatch Logs, Kinesis, and STS all appear in the core SDK modules. Spring Boot and Ktor modules then add framework entry points on top of that foundation.

AWS service coverage chart for aws-java, aws-kotlin, aws-spring-boot, aws-ktor, and examples
Core wrappers carry broad SDK coverage; framework modules expose the focused entry points applications use most often.
ServiceJava SDK v2Kotlin SDKConvenience features
S3sync/async/coroutine, CRT async, TransferManagersuspend client, file/string/byte helperslarge transfers, file upload/download, object listing
DynamoDBEnhanced Client, async table, coroutine/Flowtable/item/batch helpers, model DSLitem conversion, batch write, scan/query
SQSasync/coroutine queue operationssuspend send/receive/batch helpersqueue-URL-oriented send/receive/delete
SNStopic publish, SMS, pushsuspend publish/subscription helperstopic creation, publish, subscription
SES/SESv2email send helperssend/raw/templated/bulk helpersemail request builders and send APIs
KMSsync/async client builders, request DSLencrypt/decrypt/data key helperskey operation DSL
CloudWatch/Logsmetric/log coroutine extensionsmetricDatum/inputLogEvent DSLmetric publish, log event publish
Kinesisput/get record coroutine extensionsrecordFlow, putRecord helpersshard polling, retry/backoff
STSassumeRole, caller identity, session tokensuspend STS helpersidentity checks, temporary credentials

This table does not mean every application should carry every AWS service artifact. The library offers helpers for many services; the application chooses the service dependencies it actually runs.

The aws-java and aws-kotlin Gradle files keep most service SDK dependencies as compileOnly. For Java SDK v2 that includes S3, S3 Transfer Manager, DynamoDB Enhanced, SES/SESv2, SNS, SQS, KMS, CloudWatch, CloudWatch Logs, Kinesis, and STS. The Kotlin module follows the same idea for its Kotlin SDK service artifacts.

That lets the core modules compile helper code for broad service coverage while the runtime classpath stays application-owned. A service that only uses S3 and SQS should not carry Kinesis and SES by accident. The BOM aligns versions; the application adds the AWS SDK artifacts it really uses as implementation dependencies.

Dependency selection diagram showing BOM, compileOnly wrapper modules, application-selected runtime dependencies
compileOnly lets wrappers compile; runtime service selection remains with the application.

Use bluetape4k-aws-java when the service needs broad AWS Java SDK v2 coverage or wants the S3 large-transfer route with TransferManager, CRT-backed async clients, coroutine helpers, and Virtual Threads. This is especially useful where direct SDK use would otherwise require choosing the HTTP client, async client, executor, and coroutine bridge in every service.

Use bluetape4k-aws-kotlin when new code wants to use the AWS Kotlin SDK and keep native suspend operations. Its advantages are the CRT default engine, OkHttp escape hatch, withXxxClient lifecycle helpers, and service-specific request DSLs.

If the problem is Spring Boot 4 or Ktor 3 wiring, that belongs to Part 3. The core modules sit below those framework adapters. They reduce repeated SDK boundary code while keeping runtime dependency selection explicit.

bluetape4k-aws-java and bluetape4k-aws-kotlin solve the same integration problem from different SDK choices. The Java approach keeps AWS Java SDK v2 coverage, adds coroutine bridges, and makes the S3 CRT/TransferManager/Virtual Threads route easier to adopt. The Kotlin approach keeps the AWS Kotlin SDK’s native suspend model and adds CRT defaults, OkHttp choice, lifecycle helpers, and request DSLs.

Next, we will move up to the framework layer: Spring Boot 4 auto-configuration, SQS listeners, Ktor SigV4 plugins, and working examples.

Comments

Leave a note or reaction with your GitHub account.