Files
ulweb/plan.md
2026-07-12 01:09:33 +00:00

276 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Laravel/PHP is **not obsolete**—Laravel 13 is current, actively maintained, and still a perfectly reasonable choice. Laravel releases annually, with bug fixes for 18 months and security fixes for two years. ([Laravel][1])
For your application, though—database, MQTT-connected devices, live status, APIs, and likely a web dashboard—I would lean toward a **TypeScript stack using NestJS**.
## My recommendation
```text
Web UI: React + Next.js
Backend API: NestJS + TypeScript
Database: PostgreSQL
Database ORM: Prisma
MQTT broker: Mosquitto initially; EMQX if you outgrow it
MQTT processor: Separate NestJS worker process
Live browser UI: WebSockets or Server-Sent Events
Deployment: Docker Compose initially
Reverse proxy: Caddy, Traefik, or nginx
```
NestJS is particularly suitable because it supports conventional HTTP APIs, WebSockets, background services, and MQTT transport within one consistent framework. A Nest application can also operate as a “hybrid application,” listening for HTTP requests and messages from another transport. ([NestJS Documentation][2])
### Suggested architecture
```text
┌────────────────────┐
│ React / Next.js UI │
└─────────┬──────────┘
│ HTTPS / WebSocket
┌─────────▼──────────┐
│ NestJS API │
│ Auth, users, maps, │
│ devices, tickets │
└─────────┬──────────┘
┌──────▼──────┐
│ PostgreSQL │
└─────────────┘
MagLink / Receivers
│ MQTT over TLS
┌──────────────────┐ ┌─────────────────────┐
│ MQTT Broker │──────▶│ MQTT Worker │
│ Mosquitto / EMQX │ │ validate, store, │
└──────────────────┘ │ acknowledge, alert │
└──────────┬──────────┘
PostgreSQL / API
```
I would **not** have the web API itself be the MQTT broker. Keep these separate:
* The broker handles connections, sessions, subscriptions, retained messages, QoS, and TLS.
* Your worker subscribes to device topics, validates messages, writes database records, and publishes acknowledgments.
* Your API handles users, configuration, history, maps, device management, and commands.
* The browser receives live updates through WebSockets or Server-Sent Events—not directly from the device MQTT namespace.
This separation means restarting or deploying the web application does not disconnect every field device.
## Why NestJS is a good fit
NestJS feels somewhat like Laravel:
| Laravel concept | NestJS equivalent |
| ----------------- | --------------------------- |
| Controllers | Controllers |
| Middleware | Middleware |
| Service container | Dependency injection |
| Artisan commands | CLI commands/scripts |
| Eloquent models | Prisma models |
| Queued jobs | Worker processes/queues |
| Events/listeners | Events and message handlers |
| Laravel Echo | WebSocket gateways |
Because everything is TypeScript, you can share types between the browser, API, and MQTT message definitions. That is particularly useful for your MagLink JSON payloads:
```typescript
export interface LocateRecord {
messageId: string;
deviceSerial: string;
timestamp: string;
latitude: number;
longitude: number;
altitudeM?: number;
fixType: "NONE" | "GPS" | "DGPS" | "RTK_FLOAT" | "RTK_FIXED";
depthCm?: number;
frequencyHz?: number;
gain?: number;
signalStrength?: number;
tiltDeg?: number;
ticketNumber?: string;
}
```
You can then validate the runtime payload with Zod or NestJS validation before inserting it into PostgreSQL.
## Dont begin with microservices
I would start with one repository and three deployable processes:
```text
apps/
web/ Next.js frontend
api/ NestJS HTTP/WebSocket API
mqtt-worker/ NestJS MQTT subscriber
packages/
database/ Prisma schema and client
contracts/ Shared DTOs and MQTT schemas
common/ Shared utilities
```
They can all use the same PostgreSQL database at first. This gives you clear boundaries without introducing Kubernetes, Kafka, service discovery, distributed tracing, or multiple databases prematurely.
Run them locally with Docker Compose:
```yaml
services:
postgres:
image: postgres:17
environment:
POSTGRES_DB: maglink
POSTGRES_USER: maglink
POSTGRES_PASSWORD: development
volumes:
- postgres-data:/var/lib/postgresql/data
mosquitto:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
- "8883:8883"
volumes:
- ./infrastructure/mosquitto:/mosquitto/config
api:
build: .
command: npm run start:api
depends_on:
- postgres
mqtt-worker:
build: .
command: npm run start:mqtt-worker
depends_on:
- postgres
- mosquitto
web:
build: .
command: npm run start:web
depends_on:
- api
volumes:
postgres-data:
```
## MQTT design Id use
For your devices, use topics such as:
```text
devices/{serial}/telemetry
devices/{serial}/locates
devices/{serial}/status
devices/{serial}/commands
devices/{serial}/commands/ack
```
Every important device message should contain:
```json
{
"messageId": "01JZ...",
"schemaVersion": 1,
"deviceSerial": "ML-001234",
"timestamp": "2026-07-11T21:50:00Z",
"payload": {}
}
```
The server should enforce a unique database constraint on `messageId`. That gives you idempotency: if MQTT QoS causes redelivery, the message is not stored twice.
For important records:
1. Device publishes with QoS 1.
2. Worker validates and stores the record.
3. Database transaction commits.
4. Worker publishes an application-level acknowledgment containing the original `messageId`.
5. Device retains the local record until it receives that acknowledgment.
MQTT QoS confirms broker delivery; your application acknowledgment confirms that your system actually accepted and persisted the record.
## Authentication
Use two separate security systems:
**Web users**
* Secure HTTP-only session cookies
* OIDC/OAuth later if customers need Microsoft or Google login
* Organization-based access control in the database
**Devices**
* TLS from the beginning
* Ideally one client certificate per device
* Broker ACLs that restrict each device to its own topic hierarchy
* Never embed a common fleet-wide MQTT password in every unit
## Other good options
### ASP.NET Core
This would also be an excellent choice for you, especially since you already work with .NET MAUI. ASP.NET Core supports long-running hosted services, and MQTTnet provides both MQTT client and broker functionality with MQTT 5 support. ([Dotnet][3])
A good .NET version would be:
```text
ASP.NET Core Web API
Entity Framework Core
PostgreSQL
MQTTnet client
React or Blazor frontend
```
I would choose this over NestJS when:
* You prefer C# over JavaScript/TypeScript.
* The server will contain substantial business logic.
* You expect close integration with your MAUI application.
* Long-term static typing and compiler enforcement matter more than frontend/backend language sharing.
Honestly, **ASP.NET Core may be the strongest fit for your background**. NestJS is the more fashionable web stack, but ASP.NET Core is modern, mature, fast, strongly typed, and exceptionally well supported.
### Modern Laravel
Staying with Laravel would be the fastest path if you already know it well:
```text
Laravel 13
PostgreSQL
React or Vue starter kit
Laravel queues
Separate PHP MQTT worker
Mosquitto
```
Laravel 13 has first-party modern frontend integration and remains actively supported. ([Laravel][1]) The downside is that persistent MQTT consumers and highly concurrent real-time connections are somewhat less natural in traditional PHP deployment models than in Node.js or .NET.
### FastAPI/Python
FastAPI is excellent for data processing, GIS, ML, and quick APIs, but I would not select it merely because Python is popular. Its built-in background tasks are intended to run after HTTP responses, rather than serve as a complete durable job-processing architecture. ([FastAPI][4]) It makes sense if significant Python-specific processing will occur.
## My final choice for your project
Given your embedded devices, MQTT requirements, .NET MAUI exposure, and expectation that this may become a serious customer-facing platform:
```text
Backend: ASP.NET Core
Database: PostgreSQL + Entity Framework Core
MQTT: Mosquitto + MQTTnet worker service
Frontend: React + TypeScript
Realtime: SignalR
Local setup: Docker Compose
Hosting: A single Linux VM initially
```
I would build it as a **modular monolith**, with the MQTT ingestion worker as a separate process. That gives you modern tooling and reliable long-running MQTT handling without adding unnecessary infrastructure. NestJS would be my second choice, particularly if you want TypeScript across the entire web stack.
[1]: https://laravel.com/docs/13.x/releases?utm_source=chatgpt.com "Release Notes | Laravel 13.x - The clean stack for Artisans ..."
[2]: https://docs.nestjs.com/microservices/basics?utm_source=chatgpt.com "Microservices | NestJS - A progressive Node.js framework"
[3]: https://dotnet.github.io/MQTTnet/?utm_source=chatgpt.com "MQTTnet"
[4]: https://fastapi.tiangolo.com/tutorial/background-tasks/?utm_source=chatgpt.com "Background Tasks"