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
| Action | Path | What you should see |
|---|---|---|
| ↻ Refresh | success | the grid reloads; status ● Ticket list refreshed. |
| Edit the selected row (or New), Save ticket | success | status ● Ticket #1041 saved.; the grid refreshes |
| Clear the title, Save ticket | validation | orange banner Title is required.; status ● not saved; nothing is persisted |
| Select #1042 (no hours logged), Close ticket | domain rule | orange banner Log hours before closing. — the rule lives in Ticket.CanClose, not in the handler |
| Select #1041 (1.5 h), Close ticket | success | the 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())