Merconiq Documentation

Merconiq architecture and extension boundaries

Merconiq is currently one deployable ASP.NET Core application backed by PostgreSQL. Business capabilities are grouped in the existing projects; they are not separate deployable services or independently versioned packages. This page describes boundaries demonstrated by the current source tree, not a promise that every listed interface is a supported third-party API.

Current ownership

Capability Current code ownership Current boundary
Platform, tenancy, authorization and audit Contracts and entities in src/Merconiq.Core; HTTP authentication, authorization, tenant request context and host composition in src/Merconiq.Web; persistence and company membership implementation in src/Merconiq.Infrastructure The Web host authenticates and authorizes requests. Tenant-scoped application services and persistence enforce data scope. Audit records are produced by the shared persistence pipeline. A service interface alone is not an authorization boundary.
Inventory Domain entities, service contracts and stock operations in src/Merconiq.Core; PostgreSQL mapping and stock-count persistence in src/Merconiq.Infrastructure; HTTP/UI entry points in src/Merconiq.Web Covers item/location stock, movement history, reservations, quarantine, valuation, stock counts and transfer-in-transit records. Inventory posting remains behind existing application services and their transaction/unit-of-work behavior.
Procurement Supplier and purchase-order contracts, entities and services in src/Merconiq.Core; EF mapping/repositories in src/Merconiq.Infrastructure; endpoint/UI and DI composition in src/Merconiq.Web An approved purchase order is a procurement document; it is not itself a stock receipt or a posted financial journal. Do not infer a goods-receipt-to-ledger contract that the current source does not implement.
Sales / customer master Customer entity in src/Merconiq.Core; tenant/company ownership, migration and audit in src/Merconiq.Infrastructure; company-capability API in src/Merconiq.Web Implements independent company-owned customer records and configuration-only payment-term days. Quotations, orders, invoices, receivables, credit controls, shared customer masters and UI/import workflows remain future work.
Integrations and decision support Contracts and forecasting services in src/Merconiq.Core; webhook delivery, configuration, host services and persistence registrations in src/Merconiq.Infrastructure and src/Merconiq.Web The HTTP and webhook behavior documented in docs/API.md is part of the declared public v1 contract. Issue #355 tracks additional implementation and evidence; future, undocumented integration behavior is not thereby promised as a stable contract.
General ledger / finance Fiscal-calendar, journal and posting-policy contracts and models in src/Merconiq.Core; FiscalCalendarService, JournalPostingService and AccountPostingPolicyService in src/Merconiq.Infrastructure; company-capability API in src/Merconiq.Web The chart-of-account catalog remains company-supplied configuration. The posting kernel commits immutable, balanced JournalEntry/JournalLine rows exactly once per company/source action inside one transaction, gated by open fiscal periods and company base currency; corrections are source-linked reversals. Goods receipts, supplier invoices, payments, deliveries and sales invoices now post through this kernel with exact stock/GRNI/AP/AR/COGS effects; sales-order and purchase-order approval never post. Stock valuation records remain distinct from journal rows and reconcile through the posting services. See docs/FINANCE_POSTING.md.

These are capability ownership notes, not a claim that code is already split into module assemblies. New behavior should stay with the capability that owns its invariants. Shared platform concerns belong in existing platform code; they should not become an unowned cross-module utility layer.

Allowed dependency direction

Merconiq.Core <- Merconiq.Infrastructure <- Merconiq.Web
       ^                  ^                    ^
       +--------------- Merconiq.Tests --------+

The executable check in scripts/validate-architecture.sh enforces this project-reference graph. A new project or dependency direction is an architecture change and requires updating that check and its evidence; do not create a service-per-capability topology to add an extension.

Persistence, migrations and posting boundaries

The current persistence owner is Merconiq.Infrastructure. Its InventoryDbContext contains the implemented capability tables and the EF migrations in src/Merconiq.Infrastructure/Migrations/. There is one context and one migration stream. Do not create another context, migration assembly, or schema owner until a concrete ownership boundary and an upgrade test prove that separation is safe. The Web host currently applies this migration stream automatically only in Development.

For existing inventory postings, the persisted records have distinct roles:

Record Meaning and rule
StockTransactions Append-only stock-operation evidence. Physical movement types record quantity movement; Quarantine and QuarantineRelease record hold changes without changing on-hand quantity. Interpret TransactionType before reconciling quantities; do not count every row as physical flow. The DbContext enforces append-only behavior.
StockInHand Current on-hand quantity by item, location and lot. ReservedQuantity and QuarantinedQuantity are holds within that position, not additional physical movements. Read it as the current operational position; do not independently write it or treat it as the movement history.
StockValuationEntries Append-only moving-average valuation postings linked to valued stock movements. These are inventory valuation evidence, not general-ledger journals.
StockValuationBuckets Current weighted-average quantity/value position by item and location. Keep it consistent with the valuation entries through the existing posting service; do not use it as a second, independently writable ledger.
TransferOrders, TransferTransitEntries and TransferTransitSettlements Transfer intent and the recorded in-transit/settlement history. A transfer extension must preserve source, transit and destination movement lineage through the existing transfer workflows.

Posting code must use the existing application service/command and unit-of-work boundary so validation, tenant/company authorization, audit, concurrency, idempotency (where supported), movement/valuation consistency and transaction rollback remain in force. Reports and adapters may read authorized application results; they must not write another module’s ledger or bypass the posting service with direct DbContext writes. Never infer a general-ledger posting from an inventory valuation entry. The general-ledger journal model exists (JournalEntry, JournalLine) behind IJournalPostingService. Its only writers are the finance posting adapters listed in docs/FINANCE_POSTING.md (procurement, sales, payments, POS, fixed assets, expense claims and till approval), which reach the ledger through that kernel. Only those services may write journal rows, stock rows, receipts, invoices or payments; direct DbContext writes to posted records are blocked by append-only guards. Reconciliation, CRM, card settlement and statement services read ledger or pipeline state and never write journals.

Existing compile-time seams (limited, not a plugin API)

src/Merconiq.Web/Configuration/ApplicationServiceExtensions.cs explicitly registers existing Core service contracts such as IStockService, IPurchaseOrderService, ITransferOrderService, IDemandForecastService, IAnomalyDetectionService and IWebhookDispatcher with in-process implementations. ForecastingOptions.Implementation accepts only the managed moving-average and SSA algorithm identifiers currently supported by ForecastingImplementations.

These registrations demonstrate compile-time dependency injection inside the host. They do not establish a stable external extension API: registrations are host-owned, no service interface is identified here as a supported third-party extension contract, there is no published report/adapter contract, and no example third-party implementation has been tested through an application/database upgrade. Adding an alternate implementation today requires compiling it into and registering it from the host composition root. Any such implementation must be called only through an authorized application entry point and preserve tenant scope, audit and posting invariants. Replacing a registration does not itself provide those controls.

tests/Merconiq.Tests/Integration/ExtensionUpgradeFixturePostgreSqlTests.cs defines one bounded, test-only example around the existing MediatR CreateItemCommand pipeline. It starts at the explicitly named 20260918183139_EnforceAuditLogAppendOnly migration, applies the current forward migrations, then sends an authenticated item-creation request through the Web host with the adapter registered by dependency injection. Its assertions cover the host authorization boundary, the existing EF audit record, and the absence of writes to stock, valuation, procurement, and transfer ledgers. This is an executable example of an in-process observer around one existing operation; it does not publish a third-party report, rules, or adapter API and does not establish that arbitrary business behavior can be replaced safely.

The interfaces remain internal implementation seams until a stable external contract is explicitly designed, reviewed, versioned, and qualified. Do not introduce runtime assembly discovery, arbitrary or untrusted plugin loading, a generic plugin framework, direct cross-module ledger writes, or a second migration owner to make an example appear extensible.

Versioning and breaking changes

Code Quality and Analyzer Baselines

The web application enforces strict Roslyn analyzer baselines in both local Release builds and automated CI pipelines:

When a warning appears in these categories, developers must fix the component or contract definition rather than suppressing the diagnostic. Unrelated documentation warnings (such as CS1591) remain advisory and do not fail the build.

Observability and OpenTelemetry Metrics

Merconiq integrates OpenTelemetry for end-to-end distributed tracing and metrics:

ASP.NET Core Data Protection and Key Persistence

Production deployments persist Data Protection encryption keys to durable external storage: