Domain Design crossing Aggregate Root Boundaries - c#

I'm a novice DDD'er and struggling with my first attempt to implement some of these concepts. I am writing an application to manage some marketing campaigns for different users, so a Campaign is an obvious aggregate root, but I also have Users as an aggregate root.
public class Campaign : IDomainObject
{
public virtual int CampaignId { get; set; }
public virtual string Name { get; set; }
...
public virtual int UserId { get; set; }
...
public virtual IList<CampaignEvent> Events { get; set; }
}
public class CampaignEvent
{
public virtual int CampaignEventId { get; set; }
public virtual int CampaignId { get; set; }
public virtual int UserId { get; set; }
...
}
So now when I search for a list of campaigns to display, how do I get the User's name from the Id?
If I design the model with a database-agnostic perspective, I would add a UserName string to both objects and develop from there. However, I have to be practical and consider how this will eventually go back-and-forth to the database, and it doesn't appear that this new model will work with the ORM (presently NHibernate, but I don't believe this is possible with EF either).
What are some meet-in-the-middle solutions that will accomplish the goal of getting some user information available in the object, without compromising the concepts of DDD?

I think the key here is that you want to change the thought process to entity to entity or value object relationships.
In the way you are modeling your classes, you have a UserId as a property and are talking about it as if it was a relationship. I think what you are wanting to convey is a relationship between the Campaign and a User, a CampaignEvent and a User, etc. It is perfectly acceptable for a Campaign to be an Aggregate Root and a User to be an Aggregate Root. When you search for a campaign, it will have a relationship to a User, so how you get the name is by getting the Name from the User entity. We don't want to speak about Id's alone, as this breaks our representation of the real entity we are trying to model. So a campaign has a/a collection user/users, and a user has a/a collection of campaign/campaigns, as opposed to a campaign has a userid.

If you would like to display the user name from some result set that you got from your ORM/domain model then this is a somewhat of a problem. You should not query your domain model. So how to do it then?
Well, you should try to incorporate a query/read model. A very simple query layer should be developed. To get the data, you have a couple of options. You probably want to opt for some denormalisation along the way. You could add the user id and name as a value object to your aggregate so that it is stored in your transactional store. Your query would be able to access that and have the data readily available; else your query side would need to do a join.
You could also go with eventual consistency via a truly read-specific store that contains view-specific tables.
This approach will probably bring up some more questions but it really is not as cumbersome as it may appear.

Related

auto join/include a view in ef core through navigation property

In an asp.net core app with ef core and identity, i have multiple different user types, each having their own entity and pointing to the identity user model. In this example these are Panelist and SystemUser.
Quite often you want the contact data for either the SystemUser or the Panelist, but joining on AppUser (i.e. Panelist.Include(p => p.AppUser)) is something i'd like to avoid, since this also joins a lot of unwanted columns, especially various security related hashes. Things i'd like not to expose. Everytime you would return such a user from a controller route you would need to ensure that either AutoMapper was applied or you manually selected the necessary properties.
class AppUser : IdentityUser
{
public string LastName {get;set;}
public string FirstName {get;set;}
// inherited from IdentityUser: Email,
// PhoneNumber
}
class SystemUser
{
public AppUser AppUser {get;set;}
public string AppUserId {get;set;}
// more properties
}
class Panelist
{
public AppUser AppUser {get;set;}
public string AppUserId {get;set;}
// more properties
}
As a way out, i created a view:
-- as part of a migration in ef core
drop view if exists dbo.ContactDataView
create view dbo.ContactDataView as
select user.Id, user.FullName, user.FirstName, user.LastName, user.PhoneNumber, user.Email
from dbo.AppUser
and the matching c# side
public class ContactData
{
/// <summary>
/// user id from app user
/// </summary>
public string Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
}
in DbContext.OnModelCreating(ModelBuilder builder)
builder.Entity<Models.Views.ContactData>()
.ToView("ContactDataView")
.HasKey(contactData => contactData.Id);
The view works fine and as expected, but now i'd like to include it in one query to either of the user types through a navigation property. I am aware you could just manually join the contact data view in, but the goal (if that is possible within ef core) is to just write dataContext.SystemUusers.Include(su => su.ContactData). In addition, ef core recently also supports auto includes, which would make this even more elegant.
Just adding a navigation property (which is only meant for tables, not views as far as i understand things) fails, since this will either generate an additional foreign key or complain about using AppUserId twice.
I am aware that AutoMapper might be a solution, but it's not an option for design reasons as this would introduce mapping logic right into your domain models. This project uses AutoMapper already though, but at a much later stage.
So long story short.
was the view the right idea to begin with?
is this possible in a somewhat clean way (i.e. auto joining it), if at all
or, bite the bullet and query the view separately?
Other possible solutions all have various drawbacks. You could add computed columns for all contact data, that is very much overkill squared imho. You could duplicate the data in both user types, also not ideal. Creating two different views which does the join is also not an option, since i need these entities writable. Finally you could inherit from AppUser, but that would just shift the problem.
Thanks in advance

Creating C# objects via MySQL

I would like to get help from you, please :-)
I'm thinking about good way for programmatically creating of classes in C# via MySQL database.
In my app I'm creating composite classes. For example Student, Classroom, Room (dormitory) and so on. Class Student contains properties Classroom and Room. ClassRoom is also related to another entities in database...
public class Student
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Classroom Classroom { get; set; }
public Room Room { get; set; }
}
public class Classroom
{
public int Id { get; set; }
public string Title { get; set; }
public byte Level { get; set; }
public Teacher Teacher { get; set; }
}
...etc
Usually when I need create object of some class, I must create also another classes' objects (sometime a part of database :-) ).
I think this way is not good optimalized, BUT there are great OOP benefits. When I load all students in a DataGridView, I can manage lots of related parts... for example:
Student student = ...
string currentTeacher = student.Classroom.Teacher.LastName //... and so on.
Is OK to create all related classes' objects immediately or is better create only necessary data of current created object and another data load / create "on demand"?
Or absolutely different way? :-)
See, the idea is for you to make a query exactly like what you need an Ingress for a structural database like sql, talking a lot about the EntityFrame mappings where it is possible to query only the student object/table only by its id, however, if no process you will need the Classroom in which it belongs to you use a .Include() no entity and you would only be able to fetch the objects you will need in your request no problem mapping all entities as a bank into objects, the problem is to retrieve all of them from the relation since it only has a feature in some articles
https://learn.microsoft.com/pt-br/ef/ef6/querying/related-data
https://www.entityframeworktutorial.net/code-first/configure-entity-mappings-using-fluent-api.aspx
But if your purpose is performance, mount only the data you need in an object, just an example StudentInfo with the information you need grouped, use a Dapper to make the optimized query and make it do this mapping to your object, gaining performance
this is clear speaking of relational databases like Sql if your case is a NoSql database like MongoDb there is no problem in your mappings since it will return everything in a single document it is structured for this type of information there will be no InnerJoin cost between tables

One to many relationship doesn`t retrieve data in entity framework

I`m in process of learning C# & .NET and EF (with aspnetboilerplate) and I came up with idea to create some dummy project so I can practice. But last 4 hour Im stuck with this error and hope someone here can help me.
What I create( well at least I think I create it correctly ) is 2 class called "Ingredient" and "Master"
I want to use it for categorize Ingredient with "Master" class.
For example ingredient like
Chicken breast
chicken drumstick
Both of them belong to Meat ( witch is input in "Master" database ) and here is my code
Ingredient.cs
public class Ingrident : Entity
{
public string Name { get; set; }
public Master Master { get; set; }
public int MasterId { get; set; }
}
Master.cs
public class Master : Entity
{
public string Name { get; set; }
public List<Ingrident> Ingridents { get; set; } = new();
}
IngridientAppService.cs
public List<IngridientDto> GetIngWithParent()
{
var result = _ingRepository.GetAllIncluding(x => x.Master);
//Also I try this but doesn`t work
// var result = _ingRepository.GetAll().Where(x => x.MasterId == x.Master.Id);
return ObjectMapper.Map<List<IngridientDto>>(result);
}
IngridientDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Ingrident))]
public class IngridientDto : EntityDto
{
public string Name { get; set; }
public List<MasterDto> Master { get; set; }
public int MasterId { get; set; }
}
MasterDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Master))]
public class MasterDto : EntityDto
{
public string Name { get; set; }
}
When I created ( for last practice ) M -> M relationship this approach with .getAllIncluding work but now when I have One -> Many it won`t work.
Hope someone will be able to help me or at least give me some good hint.
Have a nice day !
Straight up the examples you are probably referring to (regarding the repository etc.) are overcomplicated and for most cases, not what you'd want to implement.
The first issue I see is that while your entities are set up for a 1-to-many relationship from Master to Ingredients, your DTOs are set up from Ingredient to Masters which definitely won't map properly.
Start with the simplest thing. Get rid of the Repository and get rid of the DTOs. I'm not sure what the base class "Entity" does, but I'm guessing it exposes a common key property called "Id". For starters I'd probably ditch that as well. When it comes to primary keys there are typically two naming approaches, every table uses a PK called "Id", or each table uses a PK with the TableName suffixed with "Id". I.e. "Id" vs. "IngredientId". Personally I find the second option makes it very clear when pairing FKs and PKs given they'd have the same name.
When it comes to representing relationships through navigation properties one important detail is ensuring navigation properties are linked to their respective FK properties if present, or better, use shadow properties for the FKs.
For example with your Ingredient table, getting rid of the Entity base class:
[Table("Ingredients")]
public class Ingredient : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IngredientId { get; set; }
public string Name { get; set; }
public int MasterId { get; set; }
[ForeignKey("MasterId")]
public virtual Master Master { get; set; }
}
This example uses EF attributes to aid in telling EF how to resolve the entity properties to respective tables and columns, as well as the relationship between Ingredient and Master. EF can work much of this out by convention, but it's good to understand and apply it explicitly because eventually you will come across situations where convention doesn't work as you expect.
Identifying the (Primary)Key and indicating it is an Identity column also tells EF to expect that the database will populate the PK automatically. (Highly recommended)
On the Master side we do something similar:
[Table("Masters")]
public class Master : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MasterId { get; set; }
public string Name { get; set; }
[InverseProperty("Master")]
public virtual ICollection<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
}
Again we denote the Primary Key, and for our Ingredients collection, we tell EF what property on the other side (Ingredient) it should use to associate to this Master's list of Ingredients using the InverseProperty attribute.
Attributes are just one option to set up the relationships etc. The other options are to use configuration classes that implement IEntityConfiguration<TEntity> (EF Core), or to configure them as part of the OnModelCreating event in the DbContext. That last option I would only recommend for very small projects as it can start to become a bit of a God method quickly. You can split it up into calls to various private methods, but you may as well just use IEntityConfiguration classes then.
Now when you go to fetch Ingredients with it's Master, or a Master with its Ingredients:
using (var context = new AppDbContext())
{
var ingredients = context.Ingredients
.Include(x => x.Master)
.Where(x => x.Master.Name.Contains("chicken"))
.ToList();
// or
var masters = context.Master
.Include(x => x.Ingredients)
.Where(x => x.Name.Contains("chicken"))
.ToList();
// ...
}
Repository patterns are a more advanced concept that have a few good reasons to implement, but for the most part they are not necessary and an anti-pattern within EF implementations. I consider Generic repositories to always be an anti-pattern for EF implementations. I.e. Repository<Ingredient> The main reason not to use repositories, especially Generic repositories with EF is that you are automatically increasing the complexity of your implementation and/or crippling the capabilities that EF can bring to your solution. As you see from working with your example, simply getting across an eager load through to the repository means writing in complex Expression<Func<TEntity>> parameters, and that just covers eager loading. Supporting projection, pagination, sorting, etc. adds even more boiler-plate complexity or limits your solution and performance without these capabilities that EF can provide out of the box.
Some good reasons to consider studying up on repository implementations /w EF:
Facilitate unit testing. (Repositories are easier to mock than DbContexts/DbSets)
Centralizing low-level data rules such as tenancy, soft deletes, and authorization.
Some bad (albeit very common) reasons to consider repositories:
Abstracting code from references or knowledge of the dependency on EF.
Abstracting the code so that EF could be substituted out.
Projecting to DTOs or ViewModels is an important aspect to building efficient and secure solutions with EF. It's not clear what "ObjectMapper" is, whether it is an Automapper Mapper instance or something else. I would highly recommend starting to grasp projection by using Linq's Select syntax to fill in a desired DTO from the models. The first key difference when using Projection properly is that when you project an object graph, you do not need to worry about eager loading related entities. Any related entity / property referenced in your projection (Select) will automatically be loaded as necessary. Later, if you want to leverage a tool like Automapper to help remove the clutter of Select statements, you will want to configure your mapping configuration then use Automapper's ProjectTo method rather than Map. ProjectTo works with EF's IQueryable implementation to resolve your mapping down to the SQL just like Select does, where Map would need to return everything eager loaded in order to populate related data. ProjectTo and Select can result in more efficient queries that can better take advantage of indexing than Eager Loading entire object graphs. (Less data over the wire between database and server/app) Map is still very useful such as scenarios where you want to copy values back from a DTO into a loaded entity.
Do it like this
public class Ingrident:Entity
{
public string Name { get; set; }
[ForeignKey(nameof(MasterId))]
public Master Master { get; set; }
public int MasterId { get; set; }
}

RavenDB Relational Model

I'm working on a side project to create a Forum built on top of RavenDB. I am currently trying to work out the relationship between Authors of Topics, and the "Last Reply" user on a topic. In a typical relational model I would simply store the FK to the User who posted the topic, and have a join off to the replies table to get the most recent replies author. This is obviously not the use case for Raven or any Document store for that matter.
What would be the most "optimal" way of pulling this off? Currently I'm tossing around a couple ideas.
Idea 1:
Save the FK of the Author on the Topic model, add a JsonIgnored User object that I will populate on the Topic load by using an Include in my session load (so one request so far from the client side, just makes the Load itself and model a bit complicated). Then possibly using a map-reduce index to get the most recent replies author (or even the same method as getting the Topic Author, so 1 or 2 queries depending).
Idea 2:
Saving both the Author and the most recent reply User on the model. Main "problem" here is the potential for stale data (say if a username changes). However that could potentially be alleviated with a background task (or simply keeping that in mind when updating a user document and going back over all posts from a user).
Example of the models in question.
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
}
public class Topic
{
public string Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
// Idea 1 Relationships
public string AuthorId { get; set; }
[JsonIgnore]
public User Author { get; set; } // Would need to be populated on loads from an Include on AuthorId
public string MostRecentReplyUserId { get; set; }
[JsonIgnore]
public User MostRecentReplyUser { get; set; } // Same as Author
// Idea 2 Relationships
public User Author { get; set; }
public User MostRecentReplyUser { get; set; }
}
Note: I would likely add a method to the User model to return a "clean" version where I scrub out things like the PasswordHash and use that on the Save for Idea 2.
Depending on your needs in case of update and query performance both ways may be the better choice.
I personally would recommend the first idea because you don't need to update existing documents when some data changes on the user records. Using include on query/load time is quite a nice feature of ravendb which can help you when retrieving nested records from the database.
Just make sure that you don't forget to include all nested documents - otherwise you may get many roundtrips.
Embedding documents (like Idea 1 but with a stored value of the users) may be better if your data processing is seperated from the data retrieval and you don't have access to the database session when converting data to be handed out to the frontend. We're using such a system that heavily relies on that (getting one input and mapping out a json pendant of the value) - this seperates data retrieval logic completely from the output (like mapping to json) logic.
Downside here: You've to make sure that existing (embedded) data get's updated whenever a user changes and the data that is transferred over the wire is more than on idea 1.

N-tier Repository POCOs - Aggregates?

Assume the following simple POCOs, Country and State:
public partial class Country
{
public Country()
{
States = new List<State>();
}
public virtual int CountryId { get; set; }
public virtual string Name { get; set; }
public virtual string CountryCode { get; set; }
public virtual ICollection<State> States { get; set; }
}
public partial class State
{
public virtual int StateId { get; set; }
public virtual int CountryId { get; set; }
public virtual Country Country { get; set; }
public virtual string Name { get; set; }
public virtual string Abbreviation { get; set; }
}
Now assume I have a simple respository that looks something like this:
public partial class CountryRepository : IDisposable
{
protected internal IDatabase _db;
public CountryRepository()
{
_db = new Database(System.Configuration.ConfigurationManager.AppSettings["DbConnName"]);
}
public IEnumerable<Country> GetAll()
{
return _db.Query<Country>("SELECT * FROM Countries ORDER BY Name", null);
}
public Country Get(object id)
{
return _db.SingleById(id);
}
public void Add(Country c)
{
_db.Insert(c);
}
/* ...And So On... */
}
Typically in my UI I do not display all of the children (states), but I do display an aggregate count. So my country list view model might look like this:
public partial class CountryListVM
{
[Key]
public int CountryId { get; set; }
public string Name { get; set; }
public string CountryCode { get; set; }
public int StateCount { get; set; }
}
When I'm using the underlying data provider (Entity Framework, NHibernate, PetaPoco, etc) directly in my UI layer, I can easily do something like this:
IList<CountryListVM> list = db.Countries
.OrderBy(c => c.Name)
.Select(c => new CountryListVM() {
CountryId = c.CountryId,
Name = c.Name,
CountryCode = c.CountryCode,
StateCount = c.States.Count
})
.ToList();
But when I'm using a repository or service pattern, I abstract away direct access to the data layer. It seems as though my options are to:
Return the Country with a populated States collection, then map over in the UI layer. The downside to this approach is that I'm returning a lot more data than is actually needed.
-or-
Put all my view models into my Common dll library (as opposed to having them in the Models directory in my MVC app) and expand my repository to return specific view models instead of just the domain pocos. The downside to this approach is that I'm leaking UI specific stuff (MVC data validation annotations) into my previously clean POCOs.
-or-
Are there other options?
How are you handling these types of things?
It really depends on the projects architecture for what we do. Usually though.. we have services above the repositories that handle this logic for you. The service decides what repositories to use to load what data. The flow is UI -> Controller -> Service -> Repositories -> DB. The UI and/or Controllers have no knowledge of the repositories or their implementation.
Also, StateCount = c.States.Count would no doubt populate the States list anyway.. wouldn't it? I'm pretty sure it will in NHibernate (with LazyLoading causing an extra select to be sent to the DB).
One option is to separate your queries from your existing infrastructure entirely. This would be an implementation of a CQRS design. In this case, you can issue a query directly to the database using a "Thin Read Layer", bypassing your domain objects. Your existing objects and ORM are actually getting in your way, and CQRS allows you to have a "command side" that is separate and possibly a totally different set of tech to your "query side", where each is designed to do it's own job without being compromised by the requirements of the other.
Yes, I'm quite literally suggesting leaving your existing architecture alone, and perhaps using something like Dapper to do this (beware of untested code sample) directly from your MVC controllers, for example:
int count =
connection.Query<int>(
"select count(*) from state where countryid = #countryid",
new { countryid = 123 } );
Honestly, your question has gave me a food for thought for a couple of days. More and more I tend to think that denormalization is the correct solution.
Look, the main point of domain driven design is to let the problem domain drive your modeling decisions. Consider the country entity in the real world. A country has a list of states. However, when you want to know how many states a certain country has, you are not going over the list of the states in the encyclopedia and count them. You are more likely to look at the country's statistics and check the number of states there.
IMHO, the same behavior should be reflected in your domain model. You can have this information in the country's property, or introduce a kind of CountryStatistics object. Whatever approach you choose, it must be a part of the country aggregate. Being in the consistency boundary of the aggregate will ensure that it holds a consistent data in case of adding or removing a state.
Some other approaches:
If the states collection is not expected to change a lot, you can
allow a bit of denormalization - add "NumberOfStates" property to the
Country object. It will optimise the query, but you'll have to make
sure the extra field holds the correct information.
If you are using NHibernate, you can use ExtraLazyLoading - it will
issue another select, but won't populate the whole collection when
Count is called. More info here:
nHibernate Collection Count

Categories