Initial commit: UlHub workspace
This commit is contained in:
275
plan.md
Normal file
275
plan.md
Normal file
@@ -0,0 +1,275 @@
|
||||
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.
|
||||
|
||||
## Don’t 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 I’d 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"
|
||||
Reference in New Issue
Block a user