Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
847e373
Create README.md
stktung Mar 19, 2025
b2e69bc
Updated edb-commerce as of 9cdb7b5691a71d5406e394ebbc33d28d70cf9280
stktung Apr 7, 2025
0159263
fixed up step 0 and 1 of the tutorial
stktung Apr 8, 2025
e16a574
Cleaned up script messages
stktung Apr 8, 2025
0f4b44c
Re-add large files under Git LFS after history rewrite
stktung Apr 8, 2025
72c8ecc
Fixed path to onCreateCommand. Added default workspace folder
stktung Apr 8, 2025
b354b04
Added cart items table to postgres projection
stktung Apr 8, 2025
e22c4f9
Added script 2 to start apps
stktung Apr 8, 2025
ac6eb28
Changed demoweb to show cart items instead of carts
stktung Apr 8, 2025
5447473
fixed oncreatecommand
stktung Apr 8, 2025
5cb5fdc
Demoweb: Shows cart items by cart id
stktung Apr 8, 2025
a98ec6f
Added tax to cart items
stktung Apr 8, 2025
b783db8
Cleaned up style
stktung Apr 8, 2025
5b169ae
Fixed calculation around tax
stktung Apr 8, 2025
39fba73
Added port forwarding config for demoweb
stktung Apr 8, 2025
4d6e51c
Updated script messages
stktung Apr 8, 2025
5644e8d
Make sure we git lfs pull during prebuild
stktung Apr 8, 2025
65b63f6
Remove unused files
stktung Apr 8, 2025
dd5b6fd
Fixed chmod command for edb-commerce
stktung Apr 8, 2025
3a7eb07
Start db and app when the container starts up automatically
stktung Apr 9, 2025
84a7d30
Use postStartCommand instead as it doesn't start a shell and just run…
stktung Apr 9, 2025
918d493
Added welcome message
stktung Apr 9, 2025
a2a0b4d
Added live datagen config file
stktung Apr 9, 2025
39659c8
Renamed script 2
stktung Apr 9, 2025
75e1543
Cleaned up script 1 messages
stktung Apr 9, 2025
6623b36
Updated messages in scripts
stktung Apr 9, 2025
79e04e9
Updated redis projection to display products from the past 24 hours
stktung Apr 9, 2025
f84fcd6
Added script 3 for live data gen
stktung Apr 9, 2025
b9b63f2
fixed script to set welcome.txt. added poststartcommand to speed thin…
stktung Apr 10, 2025
801b8f5
Try effect of postCreateCommand again.
stktung Apr 10, 2025
f1120d8
Updated devcontainer with Demo Web Page port. Also update branding an…
stktung Apr 15, 2025
b7abb88
Cleaned up and added more comments to PostgresProjection
stktung Apr 15, 2025
071fda0
Cleaned up and added more comments to RedisProjection
stktung Apr 15, 2025
b88f0f9
Updated references of EventStoreDB to KurrentDB
stktung Apr 16, 2025
b5f1cf8
Restructured scripts for the tutorial
stktung Apr 16, 2025
a4f32c2
Renamed polyglot-persistence directory to mix-and-match-database
stktung Apr 17, 2025
31b83bc
Updated dir path of polyglot-persistence to mix-and-match-database
stktung Apr 17, 2025
8044587
Small change to message in script
stktung Apr 17, 2025
2fa8f8d
Added .gitattributes to track lfs files
stktung Apr 21, 2025
216d8b9
make sure shell scripts should be commited with linux line feed instead
stktung Apr 21, 2025
ec91036
Fixed .gitattributes for lfs
stktung Apr 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 17 additions & 10 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
{
"name": "Polyglot Persistence",
"name": "Mix-and-Match Database",
"image": "mcr.microsoft.com/devcontainers/dotnet:9.0",
"workspaceFolder": "/workspaces/developer-bootcamp/mix-and-match-database/",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"containerEnv": {
"DOTNET_NOLOGO": "true"
},
"onCreateCommand": "chmod +x /workspaces/developer-bootcamp/polyglot-persistence/onCreateCommand.sh && /workspaces/developer-bootcamp/polyglot-persistence/onCreateCommand.sh",
"forwardPorts": [27017, 5432, 6379, 2113, 1113],
"onCreateCommand": "chmod +x /workspaces/developer-bootcamp/mix-and-match-database/scripts/0-prebuild-containers.sh && /workspaces/developer-bootcamp/mix-and-match-database/scripts/0-prebuild-containers.sh",
// "postCreateCommand": "sudo cp /workspaces/developer-bootcamp/.devcontainer/welcome.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt",
"postCreateCommand": "/workspaces/developer-bootcamp/mix-and-match-database/scripts/start-db.sh && /workspaces/developer-bootcamp/mix-and-match-database/scripts/start-app.sh",
"forwardPorts": [27017, 5432, 6379, 2113, 1113, 5108],
"portsAttributes": {
"2113": {
"label": "EventStoreDB Admin UI",
"onAutoForward": "notify"
"label": "KurrentDB Admin UI",
"onAutoForward": "silent"
},
"1113": {
"label": "EventStoreDB API",
"label": "KurrentDB API",
"onAutoForward": "silent"
},
"27017": {
"label": "MongoDB",
"onAutoForward": "notify"
"onAutoForward": "silent"
},
"5432": {
"label": "PostgreSQL",
"onAutoForward": "notify"
"onAutoForward": "silent"
},
"6379": {
"label": "Redis",
"onAutoForward": "notify"
}
"onAutoForward": "silent"
},
"5108": {
"label": "Demo Web Page",
"onAutoForward": "silent"
},
},
"customizations": {
"vscode": {
Expand Down
5 changes: 5 additions & 0 deletions .devcontainer/welcome.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hello!👋 Welcome to the KurrentDB Mix-and-Match Use Case Tutorial (Work in Progress)

To continue, please follow instructions found in https://docs.kurrent.io/getting-started/use-cases/mix-and-match-database-tutorial.html

Enjoy!
9 changes: 9 additions & 0 deletions mix-and-match-database/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.sh text eol=lf

tools/Kurrent.Extensions.Commerce/osx-arm64/edb-commerce filter=lfs diff=lfs merge=lfs -text
tools/Kurrent.Extensions.Commerce/osx-arm64/libduckdb.dylib filter=lfs diff=lfs merge=lfs -text
tools/Kurrent.Extensions.Commerce/osx-x64/edb-commerce filter=lfs diff=lfs merge=lfs -text
tools/Kurrent.Extensions.Commerce/osx-x64/libduckdb.dylib filter=lfs diff=lfs merge=lfs -text
tools/Kurrent.Extensions.Commerce/linux-x64/edb-commerce filter=lfs diff=lfs merge=lfs -text
tools/Kurrent.Extensions.Commerce/win-x64/edb-commerce.exe filter=lfs diff=lfs merge=lfs -text
tools/Kurrent.Extensions.Commerce/linux-arm64/edb-commerce filter=lfs diff=lfs merge=lfs -text
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}

<div class="container mt-4">
<h1>Carts Table (Postgres Projection)</h1>
<h1>Carts Table (Postgres)</h1>

<form method="get" asp-page="./Carts">
<div class="row g-3 mb-4">
Expand Down Expand Up @@ -42,62 +42,20 @@
<input type="hidden" asp-for="PageSize" />
</form>

<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead class="thead-dark">
<tr>
<th>
<a href="@Model.GetSortUrl("cart_id")">Cart ID</a>
@if (Model.SortColumn == "cart_id")
{
<span>@(Model.SortDirection == "ASC" ? "⬆" : "⬇")</span>
}
</th>
<th>
<a href="@Model.GetSortUrl("customer_id")">Customer ID</a>
@if (Model.SortColumn == "customer_id")
{
<span>@(Model.SortDirection == "ASC" ? "⬆" : "⬇")</span>
}
</th>
<th>
<a href="@Model.GetSortUrl("status")">Status</a>
@if (Model.SortColumn == "status")
{
<span>@(Model.SortDirection == "ASC" ? "⬆" : "⬇")</span>
}
</th>
<th>
<a href="@Model.GetSortUrl("created_at")">Created At</a>
@if (Model.SortColumn == "created_at")
{
<span>@(Model.SortDirection == "ASC" ? "⬆" : "⬇")</span>
}
</th>
<th>
<a href="@Model.GetSortUrl("updated_at")">Updated At</a>
@if (Model.SortColumn == "updated_at")
{
<span>@(Model.SortDirection == "ASC" ? "⬆" : "⬇")</span>
}
</th>
</tr>
</thead>
<tbody>
@if (!Model.Carts.Any())
{
<tr>
<td colspan="5" class="text-center">No carts found</td>
</tr>
}
else
{
@foreach (var cart in Model.Carts)
{
<tr>
<td>@cart.CartId</td>
<td>@(cart.CustomerId ?? "Anonymous")</td>
<td>
@if (!Model.GroupedCarts.Any())
{
<div class="alert alert-info">No carts found</div>
}
else
{
@foreach (var cart in Model.GroupedCarts)
{
<div class="card mb-4">
<div class="card-header bg-secondary bg-opacity-25">
<div class="d-flex justify-content-between align-items-center">
<div>
<h5 class="mb-0">

@switch (cart.Status)
{
case "STARTED":
Expand All @@ -113,15 +71,64 @@
<span class="badge bg-secondary">@cart.Status</span>
break;
}
</td>
<td>@cart.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")</td>
<td>@cart.UpdatedAt.ToString("yyyy-MM-dd HH:mm:ss")</td>
</tr>
}
}
</tbody>
</table>
</div>
@cart.CartId
</h5>
<div class="text-muted">
Customer: @(cart.CustomerId ?? "Anonymous") |
| Last Updated: @cart.LastUpdated.ToString("g")
</div>
</div>
<div class="text-end">
<div><strong>Items:</strong> @cart.TotalItems</div>
<div><strong>Total Quantity:</strong> @cart.TotalQuantity</div>
</div>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead>
<tr>
<th>Product ID</th>
<th>Product Name</th>
<th class="text-end">Quantity</th>
<th class="text-end">Price Per Unit</th>
<th class="text-end">Tax</th>
<th class="text-end">Subtotal</th>
</tr>
</thead>
<tbody>
@foreach (var item in cart.Items)
{
<tr>
<td>@item.ProductId</td>
<td>@item.ProductName</td>
<td class="text-end">@item.Quantity</td>
<td class="text-end">@item.PricePerUnit.ToString("C")</td>
<td class="text-end">@item.Tax.ToString("P0")</td>
<td class="text-end">@((item.Quantity * item.PricePerUnit * (1 + item.Tax)).ToString("C"))</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="card-body p-0" style="border-top-width: 2px; border-top-style: solid; border-top-color: currentColor;">
<div class="table-responsive">
<table class="table mb-0">
<tbody>
<tr>
<td colspan="6" class="text-end">
<strong>Total:</strong> @cart.Items.Sum(item => item.Quantity * item.PricePerUnit * (1 + item.Tax)).ToString("C")
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
}
}

@if (Model.TotalPages > 0)
{
Expand Down Expand Up @@ -193,15 +200,14 @@

<div class="text-center mt-2">
<small class="text-muted">
Page @Model.PageNumber of @Model.TotalPages (@Model.TotalCount items)
Page @Model.PageNumber of @Model.TotalPages (@Model.TotalCount items in @Model.GroupedCarts.Count carts)
</small>
</div>
}
</div>

@section Scripts {
<script>
// Just a small script to handle the form submission when clicking sort links
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace DemoWeb.Pages;

public class CartsModel : PageModel
{
private readonly PostgresService _postgresService;
Expand All @@ -29,7 +30,8 @@ public class CartsModel : PageModel
[BindProperty(SupportsGet = true)]
public int PageSize { get; set; } = 25;

public List<Cart> Carts { get; private set; } = new();
public List<CartItem> Carts { get; private set; } = new();
public List<GroupedCart> GroupedCarts { get; private set; } = new();
public int TotalCount { get; private set; }
public int TotalPages => (int)System.Math.Ceiling(TotalCount / (double)PageSize);

Expand Down Expand Up @@ -78,8 +80,38 @@ public async Task<IActionResult> OnPostResetAsync()

private async Task LoadDataAsync()
{
Carts = await _postgresService.GetCartsAsync(FilterOptions);
TotalCount = await _postgresService.GetCartsTotalCountAsync(FilterOptions);
// Set up filter options based on query parameters
var filterOptions = new CartFilterOptions
{
CartId = CartId,
CustomerId = CustomerId,
Status = Status,
SortColumn = SortColumn,
SortDirection = SortDirection,
Page = PageNumber,
PageSize = PageSize
};

// Get cart items with pagination
Carts = await _postgresService.GetCartItemsAsync(filterOptions);
TotalCount = await _postgresService.GetCartItemsTotalCountAsync(filterOptions);

// Group cart items by cart ID
GroupedCarts = Carts
.GroupBy(c => new { c.CartId, c.CustomerId, c.Status })
.Select(g => new GroupedCart
{
CartId = g.Key.CartId,
CustomerId = g.Key.CustomerId,
Status = g.Key.Status,
Items = g.ToList(),
TotalItems = g.Count(),
TotalQuantity = g.Sum(i => i.Quantity),
TotalPrice = g.Sum(i => i.Quantity * i.PricePerUnit),
TotalTax = g.Sum(i => i.Tax),
LastUpdated = g.Max(i => i.UpdatedAt) // Get the most recent update for the cart
})
.ToList();
}

public string GetPageUrl(int pageNumber)
Expand Down Expand Up @@ -118,4 +150,18 @@ public string GetSortUrl(string column)
page = 1
});
}
}

// Class to represent a grouped cart with its items
public class GroupedCart
{
public string CartId { get; set; } = string.Empty;
public string? CustomerId { get; set; }
public string Status { get; set; } = string.Empty;
public List<CartItem> Items { get; set; } = new();
public int TotalItems { get; set; }
public int TotalQuantity { get; set; }
public decimal TotalPrice { get; set; }
public decimal TotalTax { get; set; } // Total tax for all cart items
public DateTime LastUpdated { get; set; } // Last updated timestamp for the cart
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Polyglot Persistence Demo</title>
<title>@ViewData["Title"] - Mix-and-Match Database Demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
Expand All @@ -12,7 +12,7 @@
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/TopProducts">Polyglot Persistence Demo</a>
<a class="navbar-brand" asp-area="" asp-page="/TopProducts">Mix-and-Match Database Demo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
Expand Down
Loading