Chapter 3 — A second feature, and locking it down
Goal: add an Orders feature that shares the same database, then require a login to edit the catalog. You'll write: a second slice under
Features/Orders/, sharing the one database.
1. A second feature in the same database
The One Person Framework idea is one database for the whole product — so a second feature does not
get a second DbContext. It maps through the one the app already has.
Features/Orders/Order.cs is chapter 2's shape with different fields:
namespace Shop.Features.Orders;
public sealed class Order : Entity<Guid>
{
private Order() { } // EF Core materialization
private Order(decimal total, Guid productId, DateTime placed)
{
Id = Guid.NewGuid();
this.Total = total;
this.ProductId = productId;
this.Placed = placed;
}
public decimal Total { get; private set; }
public Guid ProductId { get; private set; }
public DateTime Placed { get; private set; }
public static Order Create(decimal total, Guid productId, DateTime placed) => new(total, productId, placed);
public void Update(decimal total, Guid productId, DateTime placed)
{
this.Total = total;
this.ProductId = productId;
this.Placed = placed;
}
}
Then the same four companions as before — OrderRequest, OrderConfiguration, the command/handler/page
files, and the list page. They're the chapter 2 files with Product swapped for Order — copy them and
change the type.
What ties it to the existing database goes in Features/Shared/AppDbContext.cs, next to Products —
the slice's namespace, and the set:
using Shop.Features.Orders; // at the top, beside the Products one
public DbSet<Order> Orders => Set<Order>(); // inside the class
That's the whole of "sharing a database": one context, one connection string, one migration history, however many features you add. Nothing else in the slice knows or cares.
Relating entities.
Order.ProductIdis a plain foreign key here. To have EF understand it as a relationship, add a navigation property and map it inOrderConfiguration:entity.HasOne<Product>().WithMany().HasForeignKey(x => x.ProductId);See Rask.Data for the full relationship shapes.
Migrate
A new table means a new migration:
rask db add AddOrder
rask db update
Run rask dev and browse to /orders — a second working CRUD feature, in the same app.db.
2. Require a login to edit the catalog
Right now anyone can create or delete products. The app already has accounts and a login; let's use them. Rask offers two gates, and you'll use both:
[Authorize]on a page — route-level. An anonymous deep-link to the page gets redirected to/login.- The
Authorizecomponent — content-level. It renders one of its slots depending on who's signed in, without leaving the page.
Gate the write pages
Add [Authorize] (from Microsoft.AspNetCore.Authorization) to the generated create / edit / delete pages —
Features/Products/CreateProduct.cs, UpdateProduct.cs, DeleteProduct.cs:
using Microsoft.AspNetCore.Authorization;
[Authorize] // ← anonymous users are redirected to /login
[Route("/products/new")]
public sealed partial class CreateProduct : Component { … }
Leave the read-only ProductsPage (/products) public so shoppers can browse.
Hide the "New / Edit / Delete" buttons from anonymous users
Route gating stops direct navigation, but you also don't want to show buttons that will just bounce to the
login page. Wrap them in the Authorize component (from Rask.Core.Components), which the accounts battery
already uses in Auth/MembersPage.cs:
Authorize[ // only rendered for signed-in users
NavLink.Href(CreateProduct)["New product"]
]
For role-specific bits — say a "Delete" button only admins should see — pass Roles:
Authorize.Roles(["admin"])[ DeleteProductButton(product.Id) ]
Verify
/ordersrenders and creating an order persists it (sameapp.dbas products).- Signed out, visiting
/products/newredirects to/login; the "New product" button isn't shown on/products. - After signing in (register an account first at
/register— the first one is the administrator), the create/edit/delete pages and buttons appear and work.
Learn more: authentication · the rask CLI
Next → Chapter 4: Background jobs