Skip to main content

LedgerJournalHeader Aggregate

Overview

The LedgerJournalHeader aggregate is the core domain model for managing all journal entries in the General Ledger system. It represents a container for multiple journal lines that form balanced accounting transactions, enforcing double-entry bookkeeping rules and coordinating the posting lifecycle.

This aggregate implements the Strategy pattern for journal-type-specific posting logic and serves as the transactional boundary for all journal operations.

Aggregate Structure

LedgerJournalHeader (Aggregate Root)
├── Properties
│ ├── JournalBatchNumber (string)
│ ├── JournalType (JournalType enumeration)
│ ├── JournalName (LedgerJournalName)
│ ├── Status (JournalStatus: Open | Posted)
│ ├── Currency (Currency)
│ ├── PostedDate (DateTimeOffset?)
│ ├── DocumentNumber (string?)
│ ├── TotalCreditAmount (Money)
│ ├── TotalDebitAmount (Money)
│ └── DefaultDimensionId (Guid?)
├── Entities
│ └── Lines (IReadOnlyCollection<LedgerJournalLine>)
└── Behaviors
├── AddLine()
├── UpdateJournalLine()
├── RemoveLine()
├── Post()
├── CreateReversalJournalHeader()
└── MarkAsReversed()

Domain Invariants

1. Balance Requirement

Rule: A journal must be balanced (total debits = total credits) before it can be posted.

Enforcement:

  • IsBalanced property checks: TotalCreditAmount == TotalDebitAmount
  • Posting strategies validate balance before allowing posting
  • Running totals automatically maintained via UpdateTotalsAfterLineAdded() and UpdateTotalsAfterLineRemoved()

Example:

// Valid - balanced journal
journal.AddLine(new LedgerJournalLine(...) { DebitAmount = 1000 });
journal.AddLine(new LedgerJournalLine(...) { CreditAmount = 1000 });
Assert.True(journal.IsBalanced); // Total DR = 1000, Total CR = 1000

// Invalid - unbalanced journal
journal.AddLine(new LedgerJournalLine(...) { DebitAmount = 1000 });
journal.AddLine(new LedgerJournalLine(...) { CreditAmount = 500 });
Assert.False(journal.IsBalanced); // Total DR = 1000, Total CR = 500
journal.Post(strategyFactory); // ❌ Fails validation

2. Posted Journals Are Immutable

Rule: Once a journal is posted (IsPosted == true), it cannot be modified or deleted.

Enforcement: All mutation methods check IsPosted and throw InvalidOperationException if true.

Protected Methods:

  • AddLine() - Cannot add lines to posted journal
  • RemoveLine() - Cannot remove lines from posted journal
  • UpdateJournalLine() - Cannot update lines on posted journal
  • Update() - Cannot update journal properties
  • ClearLines() - Cannot clear lines from posted journal
  • SetDefaultDimensions() - Cannot modify default dimensions

Example:

journal.Post(strategyFactory);
Assert.True(journal.IsPosted);

// All these operations throw InvalidOperationException
journal.AddLine(newLine); // ❌ Throws
journal.RemoveLine(lineId); // ❌ Throws
journal.UpdateJournalLine(lineId, ...); // ❌ Throws
journal.Update(...); // ❌ Throws

3. Offset Entry Handling

Rule: Lines with offset accounts automatically create balancing entries, affecting both TotalDebit and TotalCredit.

Logic:

  • If line has OffsetAccountId and is a debit, system implicitly creates credit to offset account
  • If line has OffsetAccountId and is a credit, system implicitly creates debit to offset account
  • Totals calculation accounts for these implicit entries

Example:

// Single line with offset account
journal.AddLine(new LedgerJournalLine(...) {
AccountId = cashAccountId,
DebitAmount = 1000,
OffsetAccountId = revenueAccountId,
OffsetAccountType = JournalTransactionAccountType.GLAccount
});

// Result:
// TotalDebitAmount = 1000 (from Cash debit)
// TotalCreditAmount = 1000 (implicit Revenue credit from offset)
Assert.True(journal.IsBalanced); // Automatically balanced

4. Currency Consistency

Rule: All journal lines must use the same currency as the journal header.

Enforcement:

  • Journal currency set at creation time
  • Cannot change currency if journal has existing lines
  • Lines inherit currency from journal

Example:

var journal = new LedgerJournalHeader(..., currency: usdCurrency, ...);
journal.AddLine(new LedgerJournalLine(..., currencyCode: "USD", ...)); // ✓ Valid

// Attempt to change currency after adding lines
journal.Update(
ledgerJournalNameId: nameId,
voucherSeriesId: seriesId,
currencyCode: "EUR"
); // ❌ Throws: "Cannot change currency if journal has lines"

5. Line Numbering

Rule: Journal lines are sequentially numbered starting from 1.

Enforcement:

  • NextLineNumber property automatically calculates next available number
  • AddLine() automatically assigns line numbers
  • RenumberLines() method re-sequences all lines if gaps exist

Example:

journal.AddLine(line1); // Assigned LineNumber = 1
journal.AddLine(line2); // Assigned LineNumber = 2
journal.AddLine(line3); // Assigned LineNumber = 3

journal.RemoveLine(line2.Id, renumberLines: true);
// Line 1: LineNumber = 1
// Line 3: LineNumber = 2 (renumbered)

Key Properties

Identification Properties

JournalBatchNumber (string)

  • Unique identifier for the journal batch
  • User-visible reference number
  • Used for tracking and reporting

DocumentNumber (string?)

  • Optional document number for journal
  • Can be auto-generated from number sequence
  • Distinct from voucher numbers on lines

Configuration Properties

LedgerJournalNameId (Guid)

  • Foreign key to LedgerJournalName configuration
  • Determines journal behavior, posting strategy, voucher numbering
  • Cannot be changed after journal is posted

JournalTypeId (int?)

  • Foreign key to JournalType enumeration
  • Determines posting strategy to use:
    • 0 - Daily (General journals)
    • 1 - Customer Payment
    • 2 - Vendor Payment
    • 3 - Payroll Disbursement
    • 4 - Tax Settlement

CurrencyId / CurrencyCode (Guid / string)

  • Currency for all journal lines
  • Immutable once lines are added
  • Used for monetary calculations

DefaultDimensionId (Guid?)

  • Optional default financial dimension combination
  • Applied to new lines if specified
  • Can be changed before posting

Status Properties

Status (JournalStatus)

  • Current status: Open or Posted
  • Changes from Open to Posted during posting
  • Determines mutability of journal

PostedDate (DateTimeOffset?)

  • Timestamp when journal was posted
  • null for unposted journals
  • Used for audit trail and period validation

IsPosted (bool)

  • Computed property: PostedDate.HasValue
  • Convenience check for posted status

Financial Properties

TotalCreditAmount (Money)

  • Sum of all credit amounts including implicit offset credits
  • Automatically maintained on line add/remove/update
  • Must equal TotalDebitAmount for posting

TotalDebitAmount (Money)

  • Sum of all debit amounts including implicit offset debits
  • Automatically maintained on line add/remove/update
  • Must equal TotalCreditAmount for posting

IsBalanced (bool)

  • Computed property: TotalCreditAmount == TotalDebitAmount
  • Core posting prerequisite

Reversal Properties

ReverseDate (DateTimeOffset?)

  • Date when journal was reversed
  • null for non-reversed journals

ReverseJournalId (Guid?)

  • ID of the reversal journal created
  • Links original journal to its reversal

Collection Properties

Lines (IReadOnlyCollection<LedgerJournalLine>)

  • Read-only collection of journal lines
  • Backed by private List<LedgerJournalLine>
  • Modified via aggregate methods only

LineCount (int)

  • Total number of lines in journal
  • Computed from Lines.Count

NextLineNumber (int)

  • Next available line number
  • Computed: Max(LineNumber) + 1 or 1 if empty

Core Behaviors

Line Management

AddLine(LedgerJournalLine line)

Adds a new line to the journal and updates running totals.

Preconditions:

  • Journal must not be posted

Effects:

  • Assigns sequential line number
  • Adds line to collection
  • Updates TotalCreditAmount and TotalDebitAmount
  • Accounts for offset entry if present

Example:

var line = new LedgerJournalLine(
voucher: "V001",
accountId: cashAccountId,
accountType: JournalTransactionAccountType.GLAccount,
description: "Cash receipt",
creditAmount: 0,
debitAmount: 1000,
offsetAccountType: JournalTransactionAccountType.GLAccount,
offsetAccountId: revenueAccountId,
currencyId: currencyId,
currencyCode: "USD",
transactionDate: DateTimeOffset.Now
);

journal.AddLine(line);
// Line.LineNumber = 1
// TotalDebitAmount = 1000
// TotalCreditAmount = 1000 (from offset)

UpdateJournalLine(...)

Updates an existing journal line with new values and recalculates totals.

Preconditions:

  • Journal must not be posted
  • Line must exist in journal

Effects:

  • Removes old amounts from totals
  • Updates line properties
  • Adds new amounts to totals
  • Raises LedgerJournalLineUpdatedDomainEvent

Example:

journal.UpdateJournalLine(
lineId: lineId,
voucher: "V001",
accountId: newAccountId,
accountType: JournalTransactionAccountType.GLAccount,
description: "Updated description",
creditAmount: 0,
debitAmount: 1500, // Changed from 1000
currency: usdCurrency,
transactionDate: DateTimeOffset.Now,
mainDimensionCombinationId: dimensionCombId
);

// Domain event added with old vs new values for audit trail

RemoveLine(Guid lineId, bool renumberLines = true)

Removes a line from the journal and updates totals.

Preconditions:

  • Journal must not be posted
  • Line must exist in journal

Effects:

  • Removes line from collection
  • Updates TotalCreditAmount and TotalDebitAmount
  • Optionally renumbers remaining lines
  • Raises LedgerJournalLineRemovedDomainEvent

Example:

journal.RemoveLine(lineId, renumberLines: true);
// Line removed
// Totals recalculated
// Remaining lines renumbered sequentially

RenumberLines()

Renumbers all lines sequentially starting from 1.

Usage: Called automatically by RemoveLine() or manually after bulk operations.

Example:

// Before: Line numbers 1, 3, 5, 8
journal.RenumberLines();
// After: Line numbers 1, 2, 3, 4

Posting Operations

Post(IJournalStrategyFactory strategyFactory)

Posts the journal using journal-type-specific posting strategy.

Workflow:

  1. Retrieve posting strategy for journal type
  2. Validate journal using strategy's validation rules
  3. Set PostedDate and Status = Posted
  4. Mark all lines as posted
  5. Execute strategy-specific posting logic
  6. Return success/failure result

Preconditions:

  • Journal must be balanced
  • Strategy must exist for journal type
  • Journal-specific validation must pass

Effects:

  • Sets PostedDate = DateTimeOffset.UtcNow
  • Sets Status = JournalStatus.Posted
  • Calls SetPosted() on all lines
  • Raises domain events via strategy

Example:

var result = journal.Post(strategyFactory);

if (result.IsSuccess)
{
Assert.True(journal.IsPosted);
Assert.NotNull(journal.PostedDate);
Assert.Equal(JournalStatus.Posted, journal.Status);
// Domain events dispatched by infrastructure
}
else
{
// Handle validation errors
Console.WriteLine(result.Error);
}

Posting Strategies:

  • GeneralJournalPostingStrategy - For daily/general journals
  • CustomerPaymentPostingStrategy - For customer payment journals
  • VendorPaymentPostingStrategy - For vendor payment journals
  • TaxSettlementPostingStrategy - For tax settlement journals

Reversal Operations

CreateReversalJournalHeader(string reversalJournalVoucher, string reason)

Creates a new journal header for reversing this journal.

Returns: New LedgerJournalHeader with:

  • Same journal name, type, currency
  • New journal batch number
  • Empty lines (caller must add reversed lines)

Usage: Called by reversal process to create reversal journal structure.

Example:

var reversalJournal = originalJournal.CreateReversalJournalHeader(
reversalJournalVoucher: "REV-V001",
reason: "Correction for incorrect posting"
);

// Caller adds reversed lines using LedgerJournalLine.ReverseLine()
foreach (var line in originalJournal.Lines)
{
var reversedLine = line.ReverseLine(
reversalVoucher: "REV-V001",
reasonForReversal: "Correction",
reversalDate: DateTimeOffset.Now
);
reversalJournal.AddLine(reversedLine);
}

reversalJournal.Post(strategyFactory);

MarkAsReversed(Guid reversalJournalId, DateTimeOffset reverseDate, LedgerJournalHeader reversalJournal)

Marks this journal as reversed and links to reversal journal.

Effects:

  • Sets ReverseJournalId and ReverseDate
  • Raises JournalReversedDomainEvent

Example:

originalJournal.MarkAsReversed(
reversalJournalId: reversalJournal.Id,
reverseDate: DateTimeOffset.Now,
reversalJournalForEvent: reversalJournal
);

Assert.NotNull(originalJournal.ReverseDate);
Assert.Equal(reversalJournal.Id, originalJournal.ReverseJournalId);

Voucher Management

GetLastVoucherStatus()

Determines the status of the last voucher in the journal.

Returns: Tuple of (VoucherNumber, IsBalanced)

Purpose: Used by voucher generation logic to decide whether to reuse last voucher or generate new one.

Business Rule:

  • If last voucher is balanced: Generate new voucher
  • If last voucher is unbalanced: Reuse last voucher

Example:

var (lastVoucher, isBalanced) = journal.GetLastVoucherStatus();

if (lastVoucher == null)
{
// First line - generate new voucher
}
else if (isBalanced)
{
// Last voucher complete - generate new voucher
}
else
{
// Last voucher incomplete - reuse it
}

GetVoucherBalance(string voucherNumber)

Calculates balance for a specific voucher within the journal.

Returns: VoucherBalanceResult containing:

  • TotalDebit: Sum of all debits for the voucher
  • TotalCredit: Sum of all credits for the voucher
  • IsBalanced: Whether debits equal credits

Accounts For:

  • Explicit debit/credit amounts on lines
  • Implicit amounts from offset entries

Example:

var balance = journal.GetVoucherBalance("V001");

Console.WriteLine($"Debit: {balance.TotalDebit}");
Console.WriteLine($"Credit: {balance.TotalCredit}");
Console.WriteLine($"Balanced: {balance.IsBalanced}");

Query Operations

AccountJournalTransactions(Guid accountId)

Returns all lines for a specific account.

Returns: IEnumerable<LedgerJournalLine>

Example:

var cashLines = journal.AccountJournalTransactions(cashAccountId);
var totalCashDebits = cashLines.Sum(l => l.DebitAmount);
var totalCashCredits = cashLines.Sum(l => l.CreditAmount);

GetAccountAmounts(Guid accountId)

Returns total credit and debit amounts for a specific account.

Returns: Tuple of (Credit, Debit)

Example:

var (totalCredit, totalDebit) = journal.GetAccountAmounts(cashAccountId);
var netAmount = totalDebit - totalCredit;

GetLedgerJournalLine(Guid lineId)

Retrieves a specific line by ID.

Returns: LedgerJournalLine? - null if not found

Example:

var line = journal.GetLedgerJournalLine(lineId);
if (line != null)
{
Console.WriteLine(line.GetLineSummary());
}

Maintenance Operations

Update(Guid ledgerJournalNameId, Guid? voucherSeriesId, string currencyCode)

Updates journal configuration properties.

Preconditions:

  • Journal must not be posted
  • Cannot change currency if lines exist

Example:

journal.Update(
ledgerJournalNameId: newJournalNameId,
voucherSeriesId: newSeriesId,
currencyCode: "USD"
);

ClearLines()

Removes all lines and resets totals.

Preconditions:

  • Journal must not be posted

Effects:

  • Clears line collection
  • Resets totals to zero

Example:

journal.ClearLines();
Assert.Equal(0, journal.LineCount);
Assert.Equal(0, journal.TotalDebitAmount.Amount);
Assert.Equal(0, journal.TotalCreditAmount.Amount);

RecalculateTotals()

Recalculates totals from scratch based on current lines.

Usage: Called after bulk line operations or data migrations.

Example:

// After direct line manipulation (in tests or migrations)
journal.RecalculateTotals();
Assert.True(journal.IsBalanced);

SetDefaultDimensions(Guid? defaultDimensionId)

Sets the default financial dimension combination for the journal.

Preconditions:

  • Journal must not be posted

Effects:

  • Sets DefaultDimensionId
  • Raises JournalHeaderDefaultDimensionsChangedDomainEvent

Example:

journal.SetDefaultDimensions(dimensionSetId);
Assert.Equal(dimensionSetId, journal.DefaultDimensionId);

CanBeDeleted()

Checks if journal can be deleted.

Returns: DeleteResult with success/failure

Business Rule: Posted journals cannot be deleted

Example:

var canDelete = journal.CanBeDeleted();
if (canDelete.IsSuccess)
{
journal.Delete();
}

Domain Events

LedgerJournalPostedDomainEvent

Raised when a journal is successfully posted.

Contains: Reference to posted journal

Handled By: Application layer creates GL transactions, updates subledgers

LedgerJournalLineUpdatedDomainEvent

Raised when a journal line is updated.

Contains:

  • Old and new account IDs
  • Old and new dimension combinations
  • Old and new amounts
  • Update metadata (who, when, why)

Purpose: Audit trail and change tracking

LedgerJournalLineRemovedDomainEvent

Raised when a journal line is removed.

Contains: Journal ID and line ID

Purpose: Cleanup and audit trail

JournalReversedDomainEvent

Raised when a journal is marked as reversed.

Contains: Original journal and reversal journal

Purpose: Link journals and notify downstream systems

JournalHeaderDefaultDimensionsChangedDomainEvent

Raised when default dimensions are set/changed.

Contains: Journal ID and new default dimension ID

Purpose: Update UI and tracking

LedgerJournalDeletedDomainEvent

Raised when a journal is deleted.

Contains: Reference to deleted journal

Purpose: Cleanup and audit trail

Strategy Pattern Implementation

The aggregate uses the Strategy pattern to handle journal-type-specific posting logic.

Design

Interface: IJournalPostingStrategy

Methods:

  • CanHandle(JournalType) - Checks if strategy handles journal type
  • ValidateForPosting(LedgerJournalHeader) - Type-specific validation
  • OnPosting(LedgerJournalHeader) - Type-specific posting logic

Factory: IJournalStrategyFactory - Returns appropriate strategy for journal type

Available Strategies

GeneralJournalPostingStrategy

For: Daily/General journals (JournalType.Daily)

Validation:

  • Balanced debits and credits
  • All lines have valid accounts
  • No payment details on lines

Posting Logic:

  • Raises LedgerJournalPostedDomainEvent
  • Standard GL transaction creation

CustomerPaymentPostingStrategy

For: Customer payment journals (JournalType.CustomerPayment)

Validation:

  • Payment lines must have PaymentDetails
  • Settlement amounts must match line amounts
  • Customer accounts required

Posting Logic:

  • Raises payment-specific domain events
  • Updates customer balances
  • Records settlements

VendorPaymentPostingStrategy

For: Vendor payment journals (JournalType.VendorPayment)

Validation:

  • Payment lines must have PaymentDetails
  • Settlement amounts must match line amounts
  • Vendor accounts required

Posting Logic:

  • Raises payment-specific domain events
  • Updates vendor balances
  • Records settlements

TaxSettlementPostingStrategy

For: Tax settlement journals (JournalType.TaxSettlement)

Validation:

  • Tax-specific validation rules

Posting Logic:

  • Settles tax transactions
  • Updates tax authority balances

Usage Example

// Strategy automatically selected based on journal type
var result = journal.Post(strategyFactory);

// Factory internally:
// 1. Gets journal type from journal.JournalTypeId
// 2. Returns appropriate strategy (e.g., GeneralJournalPostingStrategy)
// 3. Strategy validates and posts with type-specific logic

Integration with Other Aggregates

LedgerJournalName

  • Relationship: Many journals belong to one journal name
  • Usage: Provides configuration template for journal behavior
  • Properties Used: Journal type, voucher series, offset account defaults

Currency

  • Relationship: Each journal has one currency
  • Usage: Determines currency for all monetary amounts
  • Constraint: Cannot change currency once lines exist

NumberSequence

  • Relationship: Optional number sequence for document numbers
  • Usage: Auto-generates unique document numbers
  • Managed By: Application layer preprocessing

DimensionCombination

  • Relationship: Optional default dimension combination
  • Usage: Applied to new lines as default financial dimensions
  • Managed By: DefaultDimensionId property

Persistence Considerations

Aggregate Boundary

  • LedgerJournalHeader is the aggregate root
  • LedgerJournalLine entities are always loaded/saved with header
  • Repository operates on full aggregate (header + lines)

Loading Strategy

// Always load with lines for full aggregate
var journal = await repository.GetByIdAsync(journalId, includeLines: true);

Concurrency

  • Use optimistic concurrency with version/timestamp
  • Entire aggregate saved/updated in single transaction
  • Version check on header prevents lost updates

Cascade Operations

  • Deleting journal cascades to delete all lines
  • Updating journal does not cascade to lines (explicit line updates required)

Business Rules Summary

RuleEnforcementImpact
Journal must be balanced to postPost() method via strategiesCannot post unbalanced journal
Posted journals are immutableAll mutation methods check IsPostedCannot modify after posting
Lines must use journal currencyCurrency validation at line creationConsistent currency across journal
Line numbers are sequentialAddLine() auto-assignsEasy reference and ordering
Offset entries auto-balanceTotal calculation includes offsetsSingle-line entries can be balanced
Cannot change currency with linesUpdate() validationPrevents currency mismatch
Cannot delete posted journalsCanBeDeleted() checkAudit trail preservation

Testing Strategies

Unit Tests

Invariant Tests:

[Fact]
public void AddLine_UpdatesTotals_Correctly()
{
var journal = CreateTestJournal();
var line = CreateDebitLine(amount: 1000);

journal.AddLine(line);

Assert.Equal(1000, journal.TotalDebitAmount.Amount);
}

[Fact]
public void Post_WhenUnbalanced_Fails()
{
var journal = CreateTestJournal();
journal.AddLine(CreateDebitLine(1000));
// No credit line - unbalanced

var result = journal.Post(strategyFactory);

Assert.True(result.IsFailure);
}

State Transition Tests:

[Fact]
public void Post_Changes_StatusToPosted()
{
var journal = CreateBalancedJournal();

journal.Post(strategyFactory);

Assert.Equal(JournalStatus.Posted, journal.Status);
Assert.True(journal.IsPosted);
Assert.NotNull(journal.PostedDate);
}

Mutation Protection Tests:

[Fact]
public void AddLine_WhenPosted_Throws()
{
var journal = CreateAndPostJournal();

var exception = Assert.Throws<InvalidOperationException>(
() => journal.AddLine(CreateDebitLine(1000))
);

Assert.Contains("posted", exception.Message);
}

Integration Tests

Repository Tests:

[Fact]
public async Task Repository_SavesAndLoads_CompleteAggregate()
{
var journal = CreateJournalWithLines();
await repository.AddAsync(journal);
await unitOfWork.SaveChangesAsync();

var loaded = await repository.GetByIdAsync(journal.Id);

Assert.Equal(journal.LineCount, loaded.LineCount);
Assert.Equal(journal.TotalDebitAmount, loaded.TotalDebitAmount);
}

Posting Tests:

[Fact]
public async Task Post_CreatesGeneralJournalEntries()
{
var journal = CreateBalancedJournal();
await repository.AddAsync(journal);

journal.Post(strategyFactory);
await unitOfWork.SaveChangesAsync();

var entries = await glEntryRepository
.GetByJournalIdAsync(journal.Id);
Assert.NotEmpty(entries);
}

Common Patterns and Anti-Patterns

Pattern: Balanced Batch Entry

// ✅ Good: Balanced journal
var journal = new LedgerJournalHeader(...);
journal.AddLine(new LedgerJournalLine(...) { DebitAmount = 1000 });
journal.AddLine(new LedgerJournalLine(...) { CreditAmount = 1000 });
journal.Post(strategyFactory);

Pattern: Single-Line with Offset

// ✅ Good: Auto-balanced single line
journal.AddLine(new LedgerJournalLine(...) {
DebitAmount = 1000,
OffsetAccountId = revenueAccountId
});
// Automatically balanced via offset

Anti-Pattern: Direct Line Collection Mutation

// ❌ Bad: Bypassing aggregate methods
journal.Lines.Add(line); // Compile error - read-only collection

// ✅ Good: Use aggregate methods
journal.AddLine(line);

Anti-Pattern: Manual Total Calculation

// ❌ Bad: Recalculating totals externally
var total = journal.Lines.Sum(l => l.DebitAmount);

// ✅ Good: Trust aggregate's totals
var total = journal.TotalDebitAmount.Amount;

Anti-Pattern: Posting Without Strategy

// ❌ Bad: Bypassing strategy pattern
journal.SetPosted();
journal.PostedDate = DateTimeOffset.Now;

// ✅ Good: Use Post method with strategy
journal.Post(strategyFactory);

References

  • Domain Model: GeneralLedger.Domain/Aggregates/LedgerJournalAggregate/
  • Posting Strategies: GeneralLedger.Domain/Aggregates/LedgerJournalAggregate/Strategy/
  • Domain Events: GeneralLedger.Domain/Aggregates/LedgerJournalAggregate/Events/
  • Related Aggregates:
    • LedgerJournalName.aggregate.md
    • GeneralJournalEntry.aggregate.md
    • DimensionCombination.aggregate.md