Skip to main content

Production Architecture & Project Structure

The junior TicketOps app (SQL, rules and UI fused inside btnSave_Click) becomes a production structure — Views, Controls, Services, Domain, Data, Infrastructure, Resources, Diagnostics — with an ITicketService contract, a fake in-memory TicketService behind it, thin event handlers that read the form / call the service / show the result, and every path (success, validation, domain rule, unexpected failure) visible to the user without leaking internals.

Nothing here is deployed anywhere; it is a plain Wisej.NET 4 project that runs on your machine.

Run it

cd "LearnWisej-Samples/Production Architecture Deep Dive\Module 1\TicketOps"
dotnet run -f net10.0 --urls http://localhost:5101

Then open http://localhost:5101. (Visual Studio: open TicketOps.slnx, press F5 — the port is in Properties/launchSettings.json.)

Requirements: .NET 10 SDK, the Wisej-4 4.1.0 NuGet package.

What to try in the Open Tickets window

ActionPathWhat you should see
↻ Refreshsuccessthe grid reloads; status ● Ticket list refreshed.
Edit the selected row (or New), Save ticketsuccessstatus ● Ticket #1041 saved.; the grid refreshes
Clear the title, Save ticketvalidationorange banner Title is required.; status ● not saved; nothing is persisted
Select #1042 (no hours logged), Close ticketdomain ruleorange banner Log hours before closing. — the rule lives in Ticket.CanClose, not in the handler
Select #1041 (1.5 h), Close ticketsuccessthe row leaves the open list

An unexpected exception in any handler is caught there: its details go to ILog (written to the server console by Diagnostics/ActivityLog), and the user sees only the red banner and toast The action could not be completed. Check the log for details.

Where things live

TicketOps/
├─ Views/
│ ├─ TicketEditor.cs the screen: thin handlers, ReadDraftFromForm / ShowResult / ReportFailure
│ └─ TicketEditor.Designer.cs GENERATED-style layout (opens in the Wisej Designer) — no logic here
├─ Controls/StatusBanner reusable "● state" + banner UserControl (display only)
├─ Services/
│ ├─ ITicketService.cs the contract the screen calls
│ └─ TicketService.cs validation + close rule + persistence orchestration (no UI types)
├─ Domain/
│ ├─ Ticket.cs record + its own rule (CanClose / Close); compiles without Wisej.NET
│ ├─ TicketDraft.cs what the form collects (UI → data)
│ └─ OperationResult.cs success / safe explanation handed back to the screen
├─ Data/
│ ├─ ITicketRepository.cs persistence contract
│ └─ InMemoryTicketRepository.cs fake store, seeded, one per session
├─ Infrastructure/
│ ├─ ILog.cs cross-cutting logging contract
│ └─ AppComposition.cs who gets what: one object graph per session, constructor injection, no statics
├─ Resources/Strings.cs safe user-facing messages
├─ Diagnostics/ActivityLog.cs the ILog implementation (error details stay here)
├─ docs/ the deliverables
├─ Program.cs Wisej.NET session entry point → AppComposition
└─ Startup.cs Kestrel host (app.UseWisej())