Part 3: Storing the Treasures — Azure Data & Storage Services

Designing CloudVault’s Data Architecture


The Data Problem

Week three. CloudVault’s API is running smoothly, but now the team faces a new challenge: where should they store data?

Marcus sits in a meeting with the data team. They have three main data requirements:

  1. Account information — Structured, relational data with ACID guarantees
  2. Transaction history — High-volume, time-series data that needs global distribution
  3. Document uploads — Large files (PDFs, images) that need quick retrieval

“We could use a single database for everything,” suggests one engineer.

“But that won’t scale,” counters another. “We need different solutions for different problems.”

Marcus nods. This is the core insight: one database doesn’t fit all use cases. You need to choose the right tool for each problem.


The Data Service Spectrum

Azure offers different data services optimized for different patterns:

ServiceBest ForConsistencyScaling
SQL DatabaseRelational, ACID, complex queriesStrongVertical
Cosmos DBGlobal distribution, high throughputEventualHorizontal
Blob StorageLarge files, unstructured dataStrongUnlimited

Option 1: Azure SQL Database

Azure SQL Database is a managed relational database service. You define schemas, relationships, and constraints. It guarantees ACID properties.

When to Use SQL Database

  • You have structured, relational data
  • You need ACID guarantees (financial transactions)
  • You need complex queries with JOINs
  • Your data fits in a single region

Setting Up SQL Database

Terminal window
az sql server create \
--resource-group rg-cloudvault-prod \
--name sqlserver-cloudvault \
--admin-user sqladmin \
--admin-password "ComplexPassword123!"
az sql db create \
--resource-group rg-cloudvault-prod \
--server sqlserver-cloudvault \
--name cloudvault_db \
--service-objective S0

Connecting from Java

Add dependencies to pom.xml:

<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>12.2.0.jre11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Configure in application.yml:

spring:
datasource:
url: jdbc:sqlserver://sqlserver-cloudvault.database.windows.net:1433;database=cloudvault_db;encrypt=true;trustServerCertificate=false
username: sqladmin
password: ${DB_PASSWORD}
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
jpa:
hibernate:
ddl-auto: validate

Define your entity:

@Entity
@Table(name = "accounts")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String accountNumber;
@Column(nullable = false)
private String ownerName;
@Column(nullable = false)
private BigDecimal balance;
@CreationTimestamp
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(nullable = false)
private LocalDateTime updatedAt;
}

Cost: ~$15-50/month


Option 2: Azure Cosmos DB

Azure Cosmos DB is a globally distributed, multi-model database. It guarantees low latency (< 10ms) anywhere in the world and scales horizontally.

When to Use Cosmos DB

  • You need global distribution with low-latency reads/writes
  • You have high throughput requirements
  • You want flexible schema (documents can differ)
  • You need multiple regions with automatic failover

Setting Up Cosmos DB

Terminal window
az cosmosdb create \
--resource-group rg-cloudvault-prod \
--name cosmosdb-cloudvault \
--kind GlobalDocumentDB \
--default-consistency-level Eventual
az cosmosdb sql database create \
--resource-group rg-cloudvault-prod \
--account-name cosmosdb-cloudvault \
--name transactions_db
az cosmosdb sql container create \
--resource-group rg-cloudvault-prod \
--account-name cosmosdb-cloudvault \
--database-name transactions_db \
--name transactions \
--partition-key-path "/accountId" \
--throughput 400

Connecting from Java

Add dependencies:

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-cosmos</artifactId>
<version>4.50.0</version>
</dependency>

Define your document model:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Transaction {
@JsonProperty("id")
private String id;
@JsonProperty("accountId")
private String accountId;
@JsonProperty("type")
private String type; // DEBIT, CREDIT
@JsonProperty("amount")
private BigDecimal amount;
@JsonProperty("timestamp")
private LocalDateTime timestamp;
@JsonProperty("status")
private String status;
}

Create a repository:

@Repository
public class TransactionRepository {
@Autowired
private CosmosContainer cosmosContainer;
public void save(Transaction transaction) {
transaction.setId(UUID.randomUUID().toString());
transaction.setTimestamp(LocalDateTime.now());
cosmosContainer.createItem(transaction);
}
public List<Transaction> findByAccountId(String accountId) {
String query = "SELECT * FROM transactions t WHERE t.accountId = @accountId";
return cosmosContainer.queryItems(query, Transaction.class)
.stream().collect(Collectors.toList());
}
}

Cost: ~$25-100/month depending on throughput


Option 3: Azure Blob Storage

Azure Blob Storage is for storing large, unstructured data: files, images, videos, backups.

Setting Up Blob Storage

Terminal window
az storage account create \
--resource-group rg-cloudvault-prod \
--name cvaultstorage \
--kind StorageV2 \
--sku Standard_LRS
az storage container create \
--account-name cvaultstorage \
--name documents

Uploading Files from Java

Add dependencies:

<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.23.0</version>
</dependency>

Upload files:

@Service
public class DocumentService {
@Autowired
private BlobServiceClient blobServiceClient;
private static final String CONTAINER_NAME = "documents";
public String uploadDocument(String accountId, MultipartFile file) throws IOException {
BlobContainerClient containerClient =
blobServiceClient.getBlobContainerClient(CONTAINER_NAME);
String blobName = accountId + "/" + UUID.randomUUID() + "/" + file.getOriginalFilename();
BlobClient blobClient = containerClient.getBlobClient(blobName);
blobClient.upload(file.getInputStream(), file.getSize(), true);
return blobClient.getBlobUrl();
}
}

Cost: ~$0.018 per GB/month (very cheap)


Marcus’s Data Architecture

After analyzing CloudVault’s needs, Marcus designs:

  1. SQL Database for account data (relational, ACID)
  2. Cosmos DB for transaction history (high volume, global)
  3. Blob Storage for document uploads (files, images)

“This gives us the best of all worlds,” Marcus explains. “We get ACID guarantees where we need them, global scale for transactions, and unlimited storage for files.”


Consistency Models

A critical concept: consistency. When you write data, how quickly can other processes see it?

  • Strong Consistency: Reads always see the latest write (SQL Database)
  • Eventual Consistency: Reads might see stale data temporarily (Cosmos DB)

For financial transactions, use SQL Database. For analytics, Cosmos DB is fine.


Key Takeaways

  • SQL Database for relational, ACID-guaranteed data
  • Cosmos DB for global distribution and high throughput
  • Blob Storage for files and unstructured data
  • Choose based on your data patterns, not just the latest technology
  • Understand consistency guarantees — they affect your architecture

What’s Next?

Marcus has designed the data architecture. Now he needs to connect everything securely. In the next chapter, we’ll explore Azure’s networking services and design a secure, multi-tier architecture.

The treasures are stored. Now let’s protect them.


This is Part 3 of a 6-part series: “The Azure Ascent: A Backend Engineer’s Journey to Cloud Mastery.”