Skip to main content

Domain entities

Overview

Domain entities are objects providing data and built-in behavior for business logic, in common scenarios also used as persistence objects to retrieve and store data to database. There are several types of domain entity object ancestors available.

The core packages:

  • ASOL.Core.Domain.*

Entity

The non-base entity is based on Entity<T> base class or IEntity<T> interface. The base class is providing unique entity identifier of any type. This pattern is used ocassionally, mostly for logs, stats and associations.

public class MyExample : Entity<string>
{
/// <inheritdoc/>
public MyExample()
{
}

/// <inheritdoc/>
public MyExample(string id)
: base(id)
{
}
}

BaseEntity

The non-mandant entity is based on BaseEntity<T> base class or IBaseEntity<T> interface. The base class is providing also Released and Deleted status flags and CreatedBy, CreatedOn, ModifiedBy and ModifiedOn status info. The BaseEntityFilter class, ApplyBaseEntityFilter and TryEntityFilter methods are provided to simplify manipulation. This pattern is used sometimes, mostly for non-mandant entities, codelists, settings, slave classes and compositions.

public class MyExample : BaseEntity<string>
{
/// <inheritdoc/>
public MyExample()
{
}

/// <inheritdoc/>
public MyExample(string id, ClaimsPrincipal user)
: base(id, user)
{
}
}

BaseEntityOrganization

The mandant entity is based on BaseEntityOrganization<T> base class or IBaseEntityOrganization interface. The base class is providing also OrganizationId and OrganizationUnitId fields to specify mandant isolation. The null value means non-mandant usage, i.e. data are accessible to any mandant and also when mandant context isn't specified. The ApplyOrganizationFilter method is provided to simplify manipulation. This pattern is used very often, mostly for mandant entities, codelists and master classes.

public class MyExample : BaseEntityOrganization<string>
{
/// <inheritdoc/>
public MyExample()
{
}

/// <inheritdoc/>
public MyExample(string id, ClaimsPrincipal user, string organizationId = null, string organizationUnitId = null)
: base(id, user, organizationId, organizationUnitId)
{
}
}

ValueObject

The domain object used as property of domain entity and distinguishable by the state of its properties. The value-object can't be persisted as standalone entity.

public class MyExampleIdentification : ValueObject
{
/// <summary>
/// Initialize new MyExample identification
/// </summary>
public MyExampleIdentification()
{
}

/// <summary>
/// Initialize MyExample identification.
/// </summary>
public MyExampleIdentification(string code, string number)
{
Code = code;
Number = number;
}

[Required]
public string Code { get; set; }

[Required]
public string Number { get; set; }

public string Value { get; set; }

/// <inheritdoc/>
protected override IEnumerable<object> GetAtomicValues()
{
yield return Code;
yield return Number;
}
}

Optimistics concurency

Optimistic concurrency assumes that the update being made will be accepted, but before the change is made in the database, the original version value of the record are compared to the existing row in the database and if any changes are detected, a concurrency domain exception is raised. This is useful in situations where allowing one user's changes to overwrite another's could lead to data loss. This could happen for example, if two users are looking at a customer record, and one of the users adds a missing telephone number. The second user alters the address, but the record that they alter was retrieved before the telephone number was added by the first user. When the second user commits their change (which won't include the telephone number), the first change will be lost.

Optimistic concurrency control on domain level can be effectively managed by using a versioning system, often implemented with a DataVersion property or use IBaseEntity.ModifiedOn property in your domain entity.

public class MyExample : Entity<string>
{
/// <inheritdoc/>
public MyExample()
{
}

/// <inheritdoc/>
public MyExample(string id)
: base(id)
{
}

public int DataVersion { get; set; }
}

Before update data in your domain commands you can check if Version property (ModifiedOn) is equal to incomming data. If any changes are detected, a concurrency domain exception is raised.