Skip to content

MintPlayer/MintPlayer.Spark

Repository files navigation

MintPlayer.Spark

codecov

A low-code web application framework for .NET that eliminates boilerplate code. Inspired by Vidyano, Spark uses a PersistentObject pattern to replace traditional DTOs, repositories, and controllers with a single generic middleware.

Key Features

  • Zero DTOs - Uses PersistentObject as a universal data container
  • Zero Boilerplate - Generic middleware handles all CRUD operations
  • Configuration Over Code - Entity definitions stored as JSON files, auto-generated from C# classes
  • Dynamic UI - Angular frontend automatically renders forms and lists based on entity metadata
  • RavenDB Integration - Document database with index support for optimized queries

Technology Stack

Component Technology
Backend .NET 10.0
Frontend Angular 21
Database RavenDB 6.2+
UI Library @mintplayer/ng-bootstrap

Quick Start (AllFeatures)

The fastest way to get started is with MintPlayer.Spark.AllFeatures. Reference this single package and three source-generated methods handle all the wiring:

builder.Services.AddSparkFull(builder.Configuration);

app.UseRouting();
app.UseSparkFull(args);
app.MapSparkFull();

The source generator discovers your SparkContext, SparkUser, Actions, Recipients and Custom Actions at compile time — no generic type parameters needed.

Configure individual features via SparkFullOptions:

builder.Services.AddSparkFull(builder.Configuration, options =>
{
    options.Replication = opt =>
    {
        opt.ModuleName = "Fleet";
        opt.ModuleUrl = "https://localhost:5003";
    };
});

See the AllFeatures documentation for details.

Granular Setup

If you only need a subset of features, use the individual packages directly:

builder.Services.AddSpark(builder.Configuration, spark =>
{
    spark.UseContext<MySparkContext>();
    spark.AddActions();
    spark.AddMessaging();
    spark.AddRecipients();
});

app.UseRouting();
app.UseSpark(o => o.SynchronizeModelsIfRequested<MySparkContext>(args));
app.MapSpark();

Define Your Context

public class MySparkContext : SparkContext
{
    public IRavenQueryable<Person> People => Session.Query<Person>();
}
# Generate model files
dotnet run --spark-synchronize-model

Project Structure

MintPlayer.Spark/
├── libs/
│   ├── spark/
│   │   ├── MintPlayer.Spark/                     # Core framework library (CRUD)
│   │   └── MintPlayer.Spark.Abstractions/        # Shared interfaces and models
│   ├── authorization/
│   │   └── MintPlayer.Spark.Authorization/       # Optional auth + group-based access control
│   ├── messaging/
│   │   ├── MintPlayer.Spark.Messaging/           # Durable message bus with RavenDB persistence
│   │   └── MintPlayer.Spark.Messaging.Abstractions/  # Messaging interfaces (IMessageBus, IRecipient<T>)
│   ├── replication/
│   │   ├── MintPlayer.Spark.Replication/         # Cross-module ETL replication
│   │   └── MintPlayer.Spark.Replication.Abstractions/  # Replication interfaces and models
│   ├── subscription_worker/
│   │   ├── MintPlayer.Spark.SubscriptionWorker/  # RavenDB subscription-based background workers
│   │   └── MintPlayer.Spark.SubscriptionWorker.Abstractions/
│   ├── cron/
│   │   └── MintPlayer.Spark.Cron/                # Cron-scheduled background jobs (multi-node safe)
│   ├── webhooks/
│   │   ├── MintPlayer.Spark.Webhooks.GitHub/     # GitHub webhook integration
│   │   └── MintPlayer.Spark.Webhooks.GitHub.DevTunnel/  # Dev-only: smee.io tunnel + WebSocket client
│   ├── client/
│   │   ├── MintPlayer.Spark.Client/              # Typed HTTP client SDK
│   │   └── MintPlayer.Spark.Client.Authorization/
│   ├── all_features/
│   │   ├── MintPlayer.Spark.AllFeatures/         # All-in-one package (references all + source generator)
│   │   └── MintPlayer.Spark.AllFeatures.SourceGenerators/  # Generates AddSparkFull/UseSparkFull/MapSparkFull
│   ├── source_generators/
│   │   └── MintPlayer.Spark.SourceGenerators/    # Compile-time DI code generation
│   ├── testing/
│   │   └── MintPlayer.Spark.Testing/             # Test harness: embedded RavenDB driver, in-memory host factory
│   ├── socket_extensions/
│   │   └── MintPlayer.Dotnet.SocketExtensions/   # WebSocket read/write helpers
│   └── node_packages/                            # Angular libraries (@mintplayer/ng-spark, ng-spark-auth)
├── tests/                                        # Test projects (unit, source-generator, client, E2E)
├── Demo/
│   ├── DemoApp/                                  # Sample ASP.NET Core + Angular application
│   ├── Fleet/                                    # Fleet management demo (auth, messaging, replication)
│   ├── HR/                                       # HR demo (auth, messaging, replication)
│   └── WebhooksDemo/                             # GitHub webhooks demo application
└── docs/                                         # Documentation (guides, prd/, codecov/)

Documentation

Developer Guides

Guide Description
Getting Started PersistentObject pattern, SparkContext, entity definitions, model synchronization
Reference Attributes Entity-to-entity links, lookup references, reference selection modals
AsDetail Attributes Embedded objects, array/collection AsDetail, inline and modal editing
Queries & Sorting Index-based queries, projections, column sorting, query definitions
Attribute Grouping Two-level Tabs and Groups layout for entity forms and detail pages
Custom Attribute Renderers Replace default attribute display/editing with custom Angular components
Custom Actions Custom business operations on persistent objects with UI integration
PO/Query Aliases Friendly URLs for entities and queries (/po/car instead of /po/{guid})
TranslatedString & i18n Multi-language support for labels, descriptions, and validation messages
Authorization Optional security package, security.json, groups, permissions, XSRF
Manager & Retry Actions IManager interface, confirmation dialogs, chained retry actions
Durable Message Bus RavenDB-backed messaging with per-handler retry isolation, checkpoint support, and queue isolation
Cross-Module Synchronization Entity replication between modules with write-back support
Subscription Workers RavenDB subscription-based background processing with retry handling
Cron Jobs Cron-scheduled background jobs, UTC schedules, schedule overrides, multi-node compare-exchange locking
GitHub Webhooks React to GitHub events via typed messages, with smee.io and WebSocket dev tunneling
GitHub Webhooks — Dev Tunnel Dev-only: receive real webhook deliveries on localhost via smee.io or WebSocket forwarding from production
Docker Deployment Deploy with Docker Compose, RavenDB configuration, Traefik reverse proxy
Testing Harness Embedded RavenDB driver, in-memory Spark host factory, antiforgery-aware HTTP client, JSON fixtures, Verify defaults

Reference

Contributing

Prerequisites

Building the Project

The repo is an Nx 22 workspace spanning all .NET and Angular projects. Task graph and caching work across both stacks. CI shares build outputs across runs through a self-hosted Nx remote cache — see Nx remote cache for configuration, token gating, and the cache-poisoning consideration that's relevant if write access to this repo ever expands beyond a single maintainer.

# Clone the repository
git clone https://github.com/MintPlayer/MintPlayer.Spark.git
cd MintPlayer.Spark

# Install JS dependencies (once; npm workspaces for all ClientApps and libraries)
npm install

# Build everything the Nx graph knows about (.NET + Angular, cached)
npx nx run-many -t build

# Or just what's changed since the last green main
npx nx affected -t build

Individual projects:

# Build a specific .csproj
npx nx build Fleet

# Build an Angular library (ng-packagr)
npx nx build @mintplayer/ng-spark

# Visualize the graph
npx nx graph

Running the Demo Application

F5 from Visual Studio or plain dotnet run still works — each demo's Program.cs uses MintPlayer.AspNetCore.SpaServices.UseAngularCliServer, which spawns npm run start in ClientApp/. That script now delegates to nx run <app>:serve, so Nx orchestrates the dev-server behind the scenes:

# Start RavenDB (using Docker)
docker run -d -p 8080:8080 -e RAVEN_Security_UnsecuredAccessAllowed=PublicNetwork ravendb/ravendb

# Run the demo application
cd Demo/DemoApp/DemoApp
dotnet run

The application will be available at https://localhost:5001.

RavenDB note: the demos connect to http://localhost:8080 (appsettings.jsonSpark:RavenDb:Urls). If you point them at a standalone/local RavenDB instead of the Docker container above, make sure its PublicServerUrl is http://localhost:8080not http://host.docker.internal:8080. RavenDB advertises PublicServerUrl through its cluster topology and the client routes all subsequent requests there (caching it under Demo/**/bin/**/*.raven-cluster-topology); a host.docker.internal value the host can't reach makes every request fail with ServiceUnavailable. host.docker.internal is only correct when a container must reach a host-installed database.

Running multiple modules together (SlnLaunch)

The cross-module demos (HR + Fleet, which replicate data to each other) are launched together with the MintPlayer.SlnLaunch dotnet tool, driven by the MintPlayer.Spark.slnLaunch profile in the repo root:

dnx MintPlayer.SlnLaunch        # runs the "HR + Fleet" profile

Use 10.0.1+, which builds the projects sequentially before launching them in parallel (earlier versions could fail intermittently because the concurrent dotnet run builds raced on the MSBuild server pipe / shared output DLLs). The ASP.NET hosts come up on:

Module Host (Spark API + app)
Fleet https://localhost:5003
HR https://localhost:5005

The /spark/* endpoints live on the host port above. Each host also spawns its own Angular dev server on a separate random port (printed as ➜ Local: http://localhost:<port>/); hitting that dev-server port directly serves index.html for every path, so a request like /spark/program-units looks like a 404. Always use the host port for API/middleware requests.

Library HMR: edit any file under libs/node_packages/ng-spark/src/** or libs/node_packages/ng-spark-auth/src/** while a demo is running — changes reflect in the browser without a restart, with component state preserved. Libraries are consumed as source during dev (tsconfig path aliases resolve directly to .ts files). The ng-packagr build target on each library produces the publishable dist for npm publish; dev never consumes dist.

Model Synchronization

When you modify entity classes, regenerate the JSON model files:

cd Demo/DemoApp
dotnet run --spark-synchronize-model

This updates files in App_Data/Model/ based on your SparkContext properties.

Contribution Workflow

  1. Fork the repository
  2. Create a feature branch from master
    git checkout -b feature/your-feature-name
  3. Make your changes following the coding standards below
  4. Test your changes with the demo application
  5. Commit with clear, descriptive messages
  6. Push to your fork
  7. Open a Pull Request against master

Coding Standards

  • Follow C# coding conventions
  • Use nullable reference types (<Nullable>enable</Nullable>)
  • Use [Register] and [Inject] attributes from MintPlayer.SourceGenerators for DI
  • Add XML documentation comments to public APIs
  • Keep methods focused and testable

Project Guidelines

  • MintPlayer.Spark - Core library, no application-specific code
  • MintPlayer.Spark.Abstractions - Interfaces and models shared across projects
  • Demo/DemoApp - Sample application for testing features
  • Demo/DemoApp.Library - Example of shared entity definitions

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Low-code web framework

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors