Skip to content

Commit

Permalink
Refactor entity instance and categories service
Browse files Browse the repository at this point in the history
  • Loading branch information
ustims committed Sep 25, 2023
1 parent 42b32ca commit c5f69b7
Show file tree
Hide file tree
Showing 34 changed files with 1,702 additions and 1,765 deletions.
4 changes: 2 additions & 2 deletions CloudFabric.EAV.Domain/CloudFabric.EAV.Domain.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CloudFabric.EventSourcing.Domain" Version="0.2.0" />
<PackageReference Include="CloudFabric.Projections" Version="0.2.0" />
<PackageReference Include="CloudFabric.EventSourcing.Domain" Version="0.2.1" />
<PackageReference Include="CloudFabric.Projections" Version="0.2.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="7.0.2" />
Expand Down
30 changes: 0 additions & 30 deletions CloudFabric.EAV.Domain/Events/Instance/CategoryCreated.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

namespace CloudFabric.EAV.Domain.Events.Instance.Entity;

public record EntityCategoryPathChanged : Event
public record EntityInstanceCategoryPathUpdated : Event
{
// ReSharper disable once UnusedMember.Global
// This constructor is required for Event Store to properly deserialize from json
public EntityCategoryPathChanged()
public EntityInstanceCategoryPathUpdated()
{
}

public EntityCategoryPathChanged(Guid id,
public EntityInstanceCategoryPathUpdated(Guid id,
Guid entityConfigurationId,
Guid categoryTreeId,
string categoryPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,35 @@ namespace CloudFabric.EAV.Domain.Events.Instance.Entity;

public record EntityInstanceCreated : Event
{
public string? MachineName { get; set; }

public IReadOnlyCollection<CategoryPath>? CategoryPaths { get; set; }

public Guid EntityConfigurationId { get; set; }

public IReadOnlyCollection<AttributeInstance> Attributes { get; set; }

public Guid? TenantId { get; set; }

// ReSharper disable once UnusedMember.Global
// This constructor is required for Event Store to properly deserialize from json
public EntityInstanceCreated()
{
}

public EntityInstanceCreated(Guid id, Guid entityConfigurationId, List<AttributeInstance> attributes,
Guid? tenantId)
{
TenantId = tenantId;
Attributes = attributes;
EntityConfigurationId = entityConfigurationId;
public EntityInstanceCreated(
Guid id,
Guid entityConfigurationId,
List<AttributeInstance> attributes,
string? machineName,
Guid? tenantId,
List<CategoryPath>? categoryPaths
) {
AggregateId = id;
EntityConfigurationId = entityConfigurationId;
MachineName = machineName;
Attributes = attributes;
TenantId = tenantId;
CategoryPaths = categoryPaths;
}

public Guid EntityConfigurationId { get; set; }
public List<AttributeInstance> Attributes { get; set; }
public Guid? TenantId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CloudFabric.EAV.Domain.Models;
using CloudFabric.EventSourcing.EventStore;

namespace CloudFabric.EAV.Domain.Events.Instance.Entity;

public record EntityInstanceMachineNameUpdated : Event
{
// ReSharper disable once UnusedMember.Global
// This constructor is required for Event Store to properly deserialize from json
public EntityInstanceMachineNameUpdated()
{
}

public EntityInstanceMachineNameUpdated(Guid id, Guid entityConfigurationId, string newMachineName)
{
EntityConfigurationId = entityConfigurationId;
AggregateId = id;
NewMachineName = newMachineName;
}

public Guid EntityConfigurationId { get; set; }

public string NewMachineName { get; set; }
}
46 changes: 0 additions & 46 deletions CloudFabric.EAV.Domain/Models/Category.cs

This file was deleted.

6 changes: 3 additions & 3 deletions CloudFabric.EAV.Domain/Models/CategoryPath.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace CloudFabric.EAV.Domain.Models;

public class CategoryPath
public record CategoryPath
{
public Guid TreeId { get; set; }
public string Path { get; set; }
public string? Path { get; set; }
public Guid? ParentId { get; set; }
public string ParentMachineName { get; set; }
public string? ParentMachineName { get; set; }
}
176 changes: 173 additions & 3 deletions CloudFabric.EAV.Domain/Models/EntityInstance.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,185 @@
using System.Collections.ObjectModel;

using CloudFabric.EAV.Domain.Events.Instance.Attribute;
using CloudFabric.EAV.Domain.Events.Instance.Entity;
using CloudFabric.EventSourcing.Domain;
using CloudFabric.EventSourcing.EventStore;

namespace CloudFabric.EAV.Domain.Models;

public class EntityInstance : EntityInstanceBase
public class EntityInstance : AggregateBase
{
/// <summary>
/// For multi-tenant applications this can be used to separate records between tenants.
/// </summary>
public Guid? TenantId { get; protected set; }

/// <summary>
/// Unique human-readable identifier. Can be used in urls to help with SEO.
/// </summary>
public string? MachineName { get; protected set; }

public override string PartitionKey => EntityConfigurationId.ToString();

/// <summary>
/// An entity instance can belong to many hierarchy trees, this list will contain a record for each hierarchy tree
/// and a place (path) in it.
/// </summary>
public IReadOnlyCollection<CategoryPath> CategoryPaths { get; protected set; } =
new ReadOnlyCollection<CategoryPath>(new List<CategoryPath>());

/// <summary>
/// All attributes for this EntityInstance are validated against EntityConfiguration's attributes.
/// </summary>
public Guid EntityConfigurationId { get; protected set; }

public IReadOnlyCollection<AttributeInstance> Attributes { get; protected set; } =
new ReadOnlyCollection<AttributeInstance>(new List<AttributeInstance>());

protected EntityInstance()
{
}

public EntityInstance(IEnumerable<IEvent> events) : base(events)
{
}

public EntityInstance(Guid id, Guid entityConfigurationId, List<AttributeInstance> attributes, Guid? tenantId)
: base(id, entityConfigurationId, attributes, tenantId)
public EntityInstance(
Guid id,
Guid entityConfigurationId,
List<AttributeInstance> attributes,
string? machineName,
Guid? tenantId,
List<CategoryPath>? categoryPaths
) {
Apply(new EntityInstanceCreated(
id,
entityConfigurationId,
attributes,
machineName,
tenantId,
categoryPaths
));
}

public void AddAttributeInstance(AttributeInstance attribute)
{
Apply(new AttributeInstanceAdded(Id, attribute));
}

public void UpdateAttributeInstance(AttributeInstance attribute)
{
Apply(new AttributeInstanceUpdated(Id, EntityConfigurationId, attribute));
}

public void RemoveAttributeInstance(string attributeMachineName)
{
Apply(new AttributeInstanceRemoved(Id, EntityConfigurationId, attributeMachineName));
}

public void UpdateMachineName(string newMachineName)
{
Apply(new EntityInstanceMachineNameUpdated(Id, EntityConfigurationId, newMachineName));
}

/// <summary>
/// Updates the path of this entity inside one of hierarchy trees is belongs to.
/// </summary>
/// <param name="treeId">Id of existing category tree.</param>
/// <param name="categoryPath">Slash-separated path constructed from `machineName`s of parent EntityInstances.
/// Empty for root node. Example: Electronics/Laptops </param>
/// <param name="parentId"></param>
public void UpdateCategoryPath(Guid treeId, string categoryPath, Guid parentId)
{
Apply(new EntityInstanceCategoryPathUpdated(Id, EntityConfigurationId, treeId, categoryPath, parentId));
}

#region Event Handlers

public void On(EntityInstanceCreated @event)
{
Id = @event.AggregateId;
EntityConfigurationId = @event.EntityConfigurationId;
Attributes = new List<AttributeInstance>(@event.Attributes).AsReadOnly();
MachineName = @event.MachineName;
TenantId = @event.TenantId;
CategoryPaths = @event.CategoryPaths ?? new List<CategoryPath>().AsReadOnly();
}

public void On(AttributeInstanceAdded @event)
{
List<AttributeInstance> newCollection = new List<AttributeInstance>(Attributes) { @event.AttributeInstance };
Attributes = newCollection.AsReadOnly();
}

public void On(AttributeInstanceUpdated @event)
{
AttributeInstance? attribute = Attributes.FirstOrDefault(x =>
x.ConfigurationAttributeMachineName == @event.AttributeInstance.ConfigurationAttributeMachineName
);

if (attribute != null)
{
var newCollection = new List<AttributeInstance>(Attributes);

newCollection.Remove(attribute);
newCollection.Add(@event.AttributeInstance);

Attributes = newCollection.AsReadOnly();
}
}

public void On(AttributeInstanceRemoved @event)
{
AttributeInstance? attribute = Attributes.FirstOrDefault(x =>
x.ConfigurationAttributeMachineName == @event.AttributeMachineName
);

if (attribute != null)
{
var newCollection = new List<AttributeInstance>(Attributes);

newCollection.Remove(attribute);

Attributes = newCollection.AsReadOnly();
}
}

public void On(EntityInstanceCategoryPathUpdated @event)
{
CategoryPath? categoryPath = CategoryPaths.FirstOrDefault(x => x.TreeId == @event.CategoryTreeId);

var newCollection = new List<CategoryPath>(CategoryPaths);

if (categoryPath != null)
{
newCollection.Remove(categoryPath);
}

var newCategoryPath = categoryPath != null
? categoryPath with
{
Path = @event.CategoryPath,
ParentMachineName = @event.ParentMachineName,
ParentId = @event.ParentId
}
: new CategoryPath
{
TreeId = @event.CategoryTreeId,
Path = @event.CategoryPath,
ParentId = @event.ParentId,
ParentMachineName = @event.ParentMachineName
};

newCollection.Add(newCategoryPath);

CategoryPaths = newCollection.AsReadOnly();
}

public void On(EntityInstanceMachineNameUpdated @event)
{
MachineName = @event.NewMachineName;
}

#endregion
}
Loading

0 comments on commit c5f69b7

Please sign in to comment.