Why this matters
- Storing book cover images on the application server's disk does not scale across pods — S3 gives durable, CDN-fronted object storage with per-request access control.
- SQS decouples order placement from downstream fulfilment: the API responds fast while a worker processes the queue at its own pace.
- Secrets Manager rotates database passwords automatically; Spring Cloud AWS injects them at runtime so credentials never appear in
application.ymlor Docker images.
StreamHub production architecture (AWS)
AWS services mapped to BookStore needs
- S3 — book cover images, publisher catalog CSV uploads, generated sales report PDFs.
- SQS — order fulfilment queue, inventory sync messages, dead-letter queue for failed processing.
- Secrets Manager — database URLs, API keys, JWT signing secrets.
- Parameter Store — non-secret configuration like feature flags and service endpoints.
- Spring Cloud AWS — auto-configuration for S3, SQS, and Secrets Manager in Boot 3.x.
Dependencies
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-s3</artifactId>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-sqs</artifactId>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-secrets-manager</artifactId>
</dependency>
spring:
cloud:
aws:
region:
static: eu-west-1
credentials:
profile:
name: bookstore-prod # use IAM roles on ECS/EKS, not profiles
config:
import:
- aws-secretsmanager:bookstore/prod/database
- aws-parameterstore:/bookstore/prod/
S3 for book cover images
@Service
public class CoverImageService {
private final S3Template s3Template;
@Value("${bookstore.s3.covers-bucket}")
private String bucket;
public String uploadCover(String isbn, MultipartFile file) throws IOException {
String key = "covers/" + isbn + ".jpg";
s3Template.upload(bucket, key, file.getInputStream());
return s3Template.createSignedGetURL(bucket, key, Duration.ofHours(1)).toString();
}
public void deleteCover(String isbn) {
s3Template.deleteObject(bucket, "covers/" + isbn + ".jpg");
}
}
@RestController
@RequestMapping("/api/books/{isbn}/cover")
public class CoverImageController {
private final CoverImageService coverImageService;
@PostMapping
public ResponseEntity<CoverUploadResponse> upload(
@PathVariable String isbn,
@RequestParam("file") MultipartFile file) throws IOException {
String url = coverImageService.uploadCover(isbn, file);
return ResponseEntity.ok(new CoverUploadResponse(url));
}
}
SQS for order fulfilment
@Service
public class OrderFulfilmentPublisher {
private final SqsTemplate sqsTemplate;
@Value("${bookstore.sqs.fulfilment-queue}")
private String queueName;
public void enqueue(OrderPlacedEvent event) {
sqsTemplate.send(queueName, OrderFulfilmentMessage.from(event));
}
}
@Component
public class OrderFulfilmentListener {
private final FulfilmentService fulfilmentService;
@SqsListener("${bookstore.sqs.fulfilment-queue}")
public void processFulfilment(OrderFulfilmentMessage message) {
fulfilmentService.fulfil(message.orderId(), message.lines());
}
}
bookstore:
sqs:
fulfilment-queue: bookstore-order-fulfilment
dlq: bookstore-order-fulfilment-dlq
Secrets Manager for database credentials
# Secret stored in AWS Secrets Manager at path: bookstore/prod/database
# { "url": "jdbc:postgresql://...", "username": "bookstore", "password": "..." }
spring:
datasource:
url: ${url}
username: ${username}
password: ${password}
On ECS or EKS, attach an IAM role with secretsmanager:GetSecretValue and s3:* / sqs:* permissions scoped to BookStore resources — no static credentials in the container.
Quick recall
Everything you need if you only revisit this box.
- Spring Cloud AWS (io.awspring.cloud) auto-configures S3, SQS, and Secrets Manager clients.
- Store book covers and reports in S3; generate pre-signed URLs for temporary client access.
- Use SQS for asynchronous fulfilment work;
@SqsListenerprocesses messages with automatic deletion on success. - Load secrets via
spring.config.import: aws-secretsmanager:...— credentials never touch the image or Git. - Use IAM roles in production; reserve local AWS profiles for development only.
Test yourself
Answer these before moving on — recall is what makes it stick.