Access aggregate root child directly - domain-driven-design

I am modeling a course app, trying to play with DDD and Clean Architecture. So I have Course, which has one or more modules, and each of them has one or more lessons
I created a ModuleLessons aggregate root which is a list of lessons that belongs to a module.
I have the use case where user can access the whole list of lessons within a module, so he access an url like myapp/lessons/{module-id} and this it will endup calling something like moduleLessonsRepository.getById({module-id}) and will render to user a list of lessons which compose that module
As I understand, repository should only deal with the whole aggregate root, not child entities directly. In other words, if Lesson is not an AR, I must not have a LessonRepository.getById()
But I have another use case where user can access something like myapp/lesson/{lesson-id}
But how could I implement if I cant have a repository which returns a lesson by it's id?
I could load the ModuleLessons aggregate and then find lesson within it, but I don't have it's id to query.
I could put module id and lesson id (or maybe just a 'lesson position within it's module) on the url and use that to find the ModuleLessons AR, but I'm puting extra data on the url just to fulfill architectural constraints, is that right?
Finaly, the lesson position within it's module does mater, but this piece of data dont belong to the lesson nor to the module, that's why I created the list itself as the AR, maybe it wasn't the right decision?

Your model sounds very structural, e.g. a course consists of modules, modules consist of one or more lessons being taught as part of it, etc. It's not really solving a problem (or at least you've not described one). Could be booking a course, could be attending the lessons of a course, etc ... The other observation is that you seem to be describing what are essentially queries. You will find that most models have a conflict of interest when it comes to reading and writing, one of the main reasons CQRS came about in the first place (not suggesting you adopt that, merely pointing out the obvious). Writing happens to align with use cases and rules that must be upheld at all times (or else). Reading, on the other hand, seems to happen far more liberally, without much consideration for the past use cases that brought the queryable data about. One easy step could be to undo yourself of the shackles that say you can't return lessons by id - simply add whatever code you need to make that happen and don't feel compelled to put that in a box like a repository. Consistency is to be considered, but if the writing imposes the proper transactional boundaries, the reading won't inadvertently observe something it shouldn't. Secondary indexes can help too - they're the sort of thing that can help you find the module id based on the lesson id if you choose to continue to go down the current path.

If it is just about reading data (e.g. showing data to a user), you can always bypass the whole aggregate repository and use whatever whatever appropriate read queries you need. Only, if your use case needs to manipulate data go through the aggregate repository to retrieve a full aggregate in order to make sure transactional consistency inside this aggregate as well as business rules are applied when changing said aggregate.
Also, it should be considered that if you do you have valid use cases where you would directly change (not read) an entity inside an aggregate without the need of considering business logic that needs to be owned by the parent aggregate root, you might have missed to discover this entity being modeled as an aggregate on it's own. See also, https://stackoverflow.com/a/67250062/7730554

Related

How to reduce the higher time complexity brought by DDD

After reading the book Domain-Driven Design and some characters in the book Implementing Domain-Driven Design, I finally try to use DDD in a small service of a microservice system. And I have some questions here.
Here we have the entities Namespace, Group, and Resource. They are also aggregate roots:
As the picture pointed out, we have many Namespaces for users. And in every Namespace, we have Groups as well. And in every Group, we have Resources.
But I have a business logic:
The Group should have a unique name in its Namespace. (It is useful that the user can find the Group by its name)
To make it come true, I need to do those steps in the application layer to add a group with time complexity O(n):
Get the Namespace by its ID from the Repository of Namespace. It has a field Groups, and its type is []GroupID.
Get []Group value by []GroupID value from the Repository of Group.
Check if the name of the new group is unique in the existing Groups we get.
If it does be unique, then use the Repository of Group to save it.
But I think if I just use a sample transaction script, I can finish those in O(lg n). Because I know that I can let the field of Group name be unique in the database. How can I do it in DDD?
My thinking is:
I should add a comment in method save of the Repository interface for Group to let the user know that the save will check the name if is unique in the same Namespace.
Or we should use CQRS to check if the name of Group is unique? Another question is that maybe a Namespace may have a lot of Group. Even though we only put the ID of Group in the entity Namespace, it does cost a lot of space size. How to paginate the data?······ If we only want to get the name of Namespace by its ID, why we need get those IDs for Groups?
I do not want the DDD to limit me. But I still want to know what is the best practices. Before I know what happens, I try to avoid breaking rules.
My solution:
Thanks for the answer by #voiceofunreason. I find that it is hard to write code for set validation in the domain layer still.
#voiceofunreason tells me that I need consider the real world. I do consider and I am still confused that how to implement it to avoid breaking DDD rules. (Sorry but my question is not do we need the condition or not. My question is HOW to make the condition(or domain logic) come true without higher time complexity)
To be honest, I only have a MongoDB serving for storing all data. If I am using Transaction Script, everything is easy:
Create an index for the name of Group to make sure the names are unique.
Just insert a new Group. If the database raises any error, just refuse the request from the user.
But if I want to follow the DDD, and put the logic into the domain layer, I even do not know where to put the logic (it is easy in Transaction Script, right?). It really makes me feel blue. So my solution is:
Use DDD to split the total project into many bounded contexts.
And we do not care if we use the DDD or others in the bounded context. So tired I am.
In this bounded context, we just use Transaction Script.
Is the DDD not well to hold the condition for the set of entities, right? Because DDD always wants to get all data from the database rather than just deal in the database. Sometimes it makes the time complexity higher and I still do not know how to avoid it. Maybe I am wrong. If I am, please comment or post a new answer, thanks a lot.
The Group should have a unique name in its Namespace.
The general term for this problem is set validation. We have some collection of items, and we want to ensure that some condition holds over the entire set....
What is the business impact of having a failure
This is the key question we need to ask and it will drive our solution
in how to handle this issue as we have many choices of varying degrees
of difficulty. -- Greg Young, 2010
Some questions to consider include: is this a real constraint of the domain, or just an attempt at proofreading? Are we the authority for this data, or are we just storing a local copy of data that belongs to someone else? When we have conflicting information, can the computer determine whether the older or newer entry is in error? Does the business currently have a remediation process to use when the set condition doesn't hold? Can the business tolerate a conflict for some period of time (until end of day? minutes? nanoseconds?)
(In thinking about this last question, you may want to review Race Conditions Don't Exist, by Udi Dahan).
If the business requirement really is "we must never write conflicting entries into the collection", then any change you make must lock the collection against any potential conflicts. And this in turn has implications about, for example, how you can store the collection (trying to enforce a condition on a distributed collection is an expensive problem to have).
For the case where you can say: it makes sense to throw all of this data into a single relational database, then you might consider that the domain model is just going to make a "best effort" to avoid conflicts, and then re-enforce that with a "real" constraint in the data model.
You don't get bonus points for doing it the hard way.

What is an Aggregate Root?

No, it is not a duplication question.
I have red many sources on the subject, but still I feel like I don't fully understand it.
This is the information I have so far (from multiple sources, be it articles, videos, etc...) about what is an Aggregate and Aggregate Root:
Aggregate is a collection of multiple Value Objects\Entity references and rules.
An Aggregate is always a command model (meant to change business state).
An Aggregate represents a single unit of (database - because essentialy the changes will be persisted) work, meaning it has to be consistent.
The Aggregate Root is the interface to the external world.
An Aggregate Root must have a globally unique identifier within the system
DDD suggests to have a Repository per Aggregate Root
A simple object from an aggregate can't be changed without its AR(Aggregate Root) knowing it
So with all that in mind, lets get to the part where I get confused:
in this site it says
The Aggregate Root is the interface to the external world. All interaction with an Aggregate is via the Aggregate Root. As such, an Aggregate Root MUST have a globally unique identifier within the system. Other Entites that are present in the Aggregate but are not Aggregate Roots require only a locally unique identifier, that is, an Id that is unique within the Aggregate.
But then, in this example I can see that an Aggregate Root is implemented by a static class called Transfer that acts as an Aggregate and a static function inside called TransferedRegistered that acts as an AR.
So the questions are:
How can it be that the function is an AR, if there must be a globaly unique identifier to it, and there isn't, reason being that its a function. what does have a globaly unique identifier is the Domain Event that this function produces.
Following question - How does an Aggregate Root looks like in code? is it the event? is it the entity that is returned? is it the function of the Aggregate class itself?
In the case that the Domain Event that the function returns is the AR (As stated that it has to have that globaly unique identifier), then how can we interact with this Aggregate? the first article clearly stated that all interaction with an Aggregate is by the AR, if the AR is an event, then we can do nothing but react on it.
Is it right to say that the aggregate has two main jobs:
Apply the needed changes based on the input it received and rules it knows
Return the needed data to be persisted from AR and/or need to be raised in a Domain Event from the AR
Please correct me on any of the bullet points in the beginning if some/all of them are wrong is some way or another and feel free to add more of them if I have missed any!
Thanks for clarifying things out!
I feel like I don't fully understand it.
That's not your fault. The literature sucks.
As best I can tell, the core ideas of implementing solutions using domain driven design came out of the world of Java circa 2003. So the patterns described by Evans in chapters 5 and six of the blue book were understood to be object oriented (in the Java sense) domain modeling done right.
Chapter 6, which discusses the aggregate pattern, is specifically about life cycle management; how do you create new entities in the domain model, how does the application find the right entity to interact with, and so on.
And so we have Factories, that allow you to create instances of domain entities, and Repositories, that provide an abstraction for retrieving a reference to a domain entity.
But there's a third riddle, which is this: what happens when you have some rule in your domain that requires synchronization between two entities in the domain? If you allow applications to talk to the entities in an uncoordinated fashion, then you may end up with inconsistencies in the data.
So the aggregate pattern is an answer to that; we organize the coordinated entities into graphs. With respect to change (and storage), the graph of entities becomes a single unit that the application is allowed to interact with.
The notion of the aggregate root is that the interface between the application and the graph should be one of the members of the graph. So the application shares information with the root entity, and then the root entity shares that information with the other members of the aggregate.
The aggregate root, being the entry point into the aggregate, plays the role of a coarse grained lock, ensuring that all of the changes to the aggregate members happen together.
It's not entirely wrong to think of this as a form of encapsulation -- to the application, the aggregate looks like a single entity (the root), with the rest of the complexity of the aggregate being hidden from view.
Now, over the past 15 years, there's been some semantic drift; people trying to adapt the pattern in ways that it better fits their problems, or better fits their preferred designs. So you have to exercise some care in designing how to translate the labels that they are using.
In simple terms an aggregate root (AR) is an entity that has a life-cycle of its own. To me this is the most important point. One AR cannot contain another AR but can reference it by Id or some value object (VO) containing at least the Id of the referenced AR. I tend to prefer to have an AR contain only other VOs instead of entities (YMMV). To this end the AR is responsible for consistency and variants w.r.t. the AR. Each VO can have its own invariants such as an EMailAddress requiring a valid e-mail format. Even if one were to call contained classes entities I will call that semantics since one could get the same thing done with a VO. A repository is responsible for AR persistence.
The example implementation you linked to is not something I would do or recommend. I followed some of the comments and I too, as one commenter alluded to, would rather use a domain service to perform something like a Transfer between two accounts. The registration of the transfer is not something that may necessarily be permitted and, as such, the domain service would be required to ensure the validity of the transfer. In fact, the registration of a transfer request would probably be a Journal in an accounting sense as that is my experience. Once the journal is approved it may attempt the actual transfer.
At some point in my DDD journey I thought that there has to be something wrong since it shouldn't be so difficult to understand aggregates. There are many opinions and interpretations w.r.t. to DDD and aggregates which is why it can get confusing. The other aspect is, in IMHO, that there is a fair amount of design involved that requires some creativity and which is based on an understanding of the domain itself. Creativity cannot be taught and design falls into the realm of tacit knowledge. The popular example of tacit knowledge is learning to ride a bike. Now, we can read all we want about how to ride a bike and it may or may not help much. Once we are on the bike and we teach ourselves to balance then we can make progress. Then there are people who end up doing absolutely crazy things on a bike and even if I read how to I don't think that I'll try :)
Keep practicing and modelling until it starts to make sense or until you feel comfortable with the model. If I recall correctly Eric Evans mentions in the Blue Book that it may take a couple of designs to get the model closer to what we need.
Keep in mind that Mike Mogosanu is using a event sourcing approach but in any case (without ES) his approach is very good to avoid unwanted artifacts in mainstream OOP languages.
How can it be that the function is an AR, if there must be a globaly unique identifier to it, and there isn't, reason being that
its a function. what does have a globaly unique identifier is the
Domain Event that this function produces.
TransferNumber acts as natural unique ID; there is also a GUID to avoid the need a full Value Object in some cases.
There is no unique ID state in the computer memory because it is an argument but think about it; why you want a globaly unique ID? It is just to locate the root element and its (non unique ID) childrens for persistence purposes (find, modify or delete it).
Order A has 2 order lines (1 and 2) while Order B has 4 order lines (1,2,3,4); the unique identifier of order lines is a composition of its ID and the Order ID: A1, B3, etc. It is just like relational schemas in relational databases.
So you need that ID just for persistence and the element that goes to persistence is a domain event expressing the changes; all the changes needed to keep consistency, so if you persist the domain event using the global unique ID to find in persistence what you have to modify the system will be in a consistent state.
You could do
var newTransfer = New Transfer(TransferNumber); //newTransfer is now an AG with a global unique ID
var changes = t.RegisterTransfer(Debit debit, Credit credit)
persistence.applyChanges(changes);
but what is the point of instantiate a object to create state in the computer memory if you are not going to do more than one thing with this object? It is pointless and most of OOP detractors use this kind of bad OOP design to criticize OOP and lean to functional programming.
Following question - How does an Aggregate Root looks like in code? is it the event? is it the entity that is returned? is it the function
of the Aggregate class itself?
It is the function itself. You can read in the post:
AR is a role , and the function is the implementation.
An Aggregate represents a single unit of work, meaning it has to be consistent. You can see how the function honors this. It is a single unit of work that keeps the system in a consistent state.
In the case that the Domain Event that the function returns is the AR (As stated that it has to have that globaly unique identifier),
then how can we interact with this Aggregate? the first article
clearly stated that all interaction with an Aggregate is by the AR, if
the AR is an event, then we can do nothing but react on it.
Answered above because the domain event is not the AR.
4 Is it right to say that the aggregate has two main jobs: Apply the
needed changes based on the input it received and rules it knows
Return the needed data to be persisted from AR and/or need to be
raised in a Domain Event from the AR
Yes; again, you can see how the static function honors this.
You could try to contat Mike Mogosanu. I am sure he could explain his approach better than me.

Find aggregate root usages across the domain

As a matter of fact, Entities and even Value Objects may contain references to Aggregate Roots.
Additionally as per definition, Aggregate Roots stand on their own, the have intrinsically a Repository where I not at last could delete that Aggregate Root.
As a requirement of my GUI / Workflow, the customer wants to see where a particular Aggregate Root is referenced, not least because he wants / should be able to check whether he can delete that AR "safely".
My current design only has the navigation from the Entity in question towards the other AggregateRoot, so there's, at the moment, no simple way to find the opposite direction.
As this is surely not a single case, I wonder how this is done usually?
Addendum:
Consider the following example; we have an Address as Entity, and a Value Object HomeVisit containing date and Address address (just for the simplicity).
Until now, there is no modelling need to be able to navigate from Address to HomeVisit, even more since bidirectional associations are discouraged in general.
But you should see the use case now: For any reason I might need to be able to find out where an Address is currently used prior to delete or even modify it (maybe a service technician is currently on its way to that address and I need to be aware of that).
You can argue that for these cases there must be a Service or similar to find that out, but imagine there's a third party module which brings the HomeVisit VO and makes use of the Address somehow anonymously; at least that's the way I would like it to implement.
It seems like the answer is: via Domain or Application Services, involving respective Repositories whereas necessary.
So, the check for references has to be kinda hard-coded with an AddressService, having a method like deleteAddressByIdentifier. This method then needs to check or invoke a method isTechnicianOnWayToAddress() which again queries Repositories accordingly; or whatever is needed to fulfil the goal.

How should I enforce relationships and constraints between aggregate roots?

I have a couple questions regarding the relationship between references between two aggregate roots in a DDD model. Refer to the typical Customer/Order model diagrammed below.
First, should references between the actual object implementation of aggregates always be done through ID values and not object references? For example if I want details on the customer of an Order I would need to take the CustomerId and pass it to a ICustomerRepository to get a Customer rather then setting up the Order object to return a Customer directly correct? I'm confused because returning a Customer directly seems like it would make writing code against the model easier, and is not much harder to setup if I am using an ORM like NHibernate. Yet I'm fairly certain this would be violating the boundaries between aggregate roots/repositories.
Second, where and how should a cascade on delete relationship be enforced for two aggregate roots? For example say I want all the associated orders to be deleted when a customer is deleted. The ICustomerRepository.DeleteCustomer() method should not be referencing the IOrderRepostiory should it? That seems like that would be breaking the boundaries between the aggregates/repositories? Should I instead have a CustomerManagment service which handles deleting Customers and their associated Orders which would references both a IOrderRepository and ICustomerRepository? In that case how can I be sure that people know to use the Service and not the repository to delete Customers. Is that just down to educating them on how to use the model correctly?
First, should references between aggregates always be done through ID values and not actual object references?
Not really - though some would make that change for performance reasons.
For example if I want details on the customer of an Order I would need to take the CustomerId and pass it to a ICustomerRepository to get a Customer rather then setting up the Order object to return a Customer directly correct?
Generally, you'd model 1 side of the relationship (eg., Customer.Orders or Order.Customer) for traversal. The other can be fetched from the appropriate Repository (eg., CustomerRepository.GetCustomerFor(Order) or OrderRepository.GetOrdersFor(Customer)).
Wouldn't that mean that the OrderRepository would have to know something about how to create a Customer? Wouldn't that be beyond what OrderRepository should be responsible for...
The OrderRepository would know how to use an ICustomerRepository.FindById(int). You can inject the ICustomerRepository. Some may be uncomfortable with that, and choose to put it into a service layer - but I think that's overkill. There's no particular reason repositories can't know about and use each other.
I'm confused because returning a Customer directly seems like it would make writing code against the model easier, and is not much harder to setup if I am using an ORM like NHibernate. Yet I'm fairly certain this would be violating the boundaries between aggregate roots/repositories.
Aggregate roots are allowed to hold references to other aggregate roots. In fact, anything is allowed to hold a reference to an aggregate root. An aggregate root cannot hold a reference to a non-aggregate root entity that doesn't belong to it, though.
Eg., Customer cannot hold a reference to OrderLines - since OrderLines properly belongs as an entity on the Order aggregate root.
Second, where and how should a cascade on delete relationship be enforced for two aggregate roots?
If (and I stress if, because it's a peculiar requirement) that's actually a use case, it's an indication that Customer should be your sole aggregate root. In most real-world systems, however, we wouldn't actually delete a Customer that has associated Orders - we may deactivate them, move their Orders to a merged Customer, etc. - but not out and out delete the Orders.
That being said, while I don't think it's pure-DDD, most folks will allow some leniency in following a unit of work pattern where you delete the Orders and then the Customer (which would fail if Orders still existed). You could even have the CustomerRepository do the work, if you like (though I'd prefer to make it more explicit myself). It's also acceptable to allow the orphaned Orders to be cleaned up later (or not). The use case makes all the difference here.
Should I instead have a CustomerManagment service which handles deleting Customers and their associated Orders which would references both a IOrderRepository and ICustomerRepository? In that case how can I be sure that people know to use the Service and not the repository to delete Customers. Is that just down to educating them on how to use the model correctly?
I probably wouldn't go a service route for something so intimately tied to the repository. As for how to make sure a service is used...you just don't put a public Delete on the CustomerRepository. Or, you throw an error if deleting a Customer would leave orphaned Orders.
Another option would be to have a ValueObject describing the association between the Order and the Customer ARs, VO which will contain the CustomerId and additional information you might need - name,address etc (something like ClientInfo or CustomerData).
This has several advantages:
Your ARs are decoupled - and now can be partitioned, stored as event streams etc.
In the Order ARs you usually need to keep the information you had about the customer at the time of the order creation and not reflect on it any future changes made to the customer.
In almost all the cases the information in the value object will be enough to perform the read operations ( display customer info with the order ).
To handle the Deletion/deactivation of a Customer you have the freedom to chose any behavior you like. You can use DomainEvents and publish a CustomerDeleted event for which you can have a handler that moves the Orders to an archive, or deletes them or whatever you need. You can also perform more than one operation on that event.
If for whatever reason DomainEvents are not your choice you can have the Delete operation implemented as a service operation and not as a repository operation and use a UOW to perform the operations on both ARs.
I have seen a lot of problems like this when trying to do DDD and i think that the source of the problems is that developers/modelers have a tendency to think in DB terms. You ( we :) ) have a natural tendency to remove redundancy and normalize the domain model. Once you get over it and allow your model to evolve and implicate the domain expert(s) in it's evolution you will see that it's not that complicated and it's quite natural.
UPDATE: and a similar VO - OrderInfo can be placed inside the Customer AR if needed, with only the needed information - order total, order items count etc.

Aggregate roots depend on the use case so does that mean that we might end up with really a lots of repositories?

Ive heard a lots that aggregate roots depend on the use case. But what does that mean in coding context ?
You have a service class which offcourse hold methods (use cases) that gonna accomplish something in a repository. Great, so you use a repository which is equal to an aggregate root to perform your querying.
Now you need to perform some other kind of operation which use totally different use case than the first service class but use the same entities.
Here the representation :
Entities: Customer, Orders, LineOrder
Service 1: Add new customers, Delete some customers, retrieve customer orders
Here the aggregate root seem to be Customer because you need this repository to perform thoses use cases.
Service 2: Retrieve customer from an actual order
Here the aggregate root seem to be Order because you need this repository to perform this use case.
If i am wrong please correct me. Now that mean you have 2 aggregates roots.
Now my question is, since aggregate roots depend on the use case does that mean that we might end up with really a lots of repositories if you end up having lots of use cases ?
The above example was probably not the best example... so lets say we have a Journal which hold JournalEntries which each entries hold Tasks, Problems and Notes. (This is in the context of telling to a system what have been done to a project)
Does that mean that im gonna end up with 2 repository ? (Journal, JournalEntry)
In the use cases where i need to add new tasks, problems and notes from an journal entry ?
(Can be seen as a service)
Or might end up with 4 repository. (Journal, Task, Problems, Notes)
In the use cases where i need to access directment task, problems and notes ?
(Can be seen as another service)
But that would mean if i need both of theses services (that actually hold the use cases) that i actually need 5 repository to be able to perform use cases in both of them ?
Thanks.
Hi I saw your post and thought I may give you my opion. First I must say I've been doing DDD in project for three years now, so I'm not an expert. But I'm currently working in a project as an architect an coaching developers in DDD, and I must say it isn't a walk in the park... I don't know how many times I've refactored the model and Entity relationships.
But my experience is that you endup with some repositories (more than few but not many). My Aggregates usually contains a few classes and the Aggregate object graph isn't that deep (if you know what I mean).
But I try to be concrete:
1) Aggregate roots are defined by your needs. I mean if you feel that you need that Tasks object through Journal to often, then maybe thats a sign for it to be upgraded as a aggregate root.
2) But everything cannot be aggregate roots, so try to capsulate object that are tight related. Notes seems like a candidate for being own by a root object. You'd probably always relate Notes to the root or it loses its context. Notes cannot live by itself.
3) Remember that Aggregates are used for splitting up large complex domains into smaller "islands" that take care of thier inhabbitants. Its important to not make your domain more complex than it is.
4) You don't know how your model look likes before you've reached far into the project implementation phase. If you realize that some repositories aren't used that much, they may be candidates for merging into other root object (if they have that kind of relationship). You can break out objects that are used so much through root object without its context. I mean for example if Journal are aggregate root and contains Notes and Tasks. After a while you model grows and maybe Tasks
have assoications to Action and ActionHistory and User and Rule and Permission. Now I just throw out a bunch om common objects in a rule/action/user permission functionality. Maybe this result in usecases that approach Tasks from another angle, "View all Tasks performed by this User" etc. Tasks get more involved in some kind of State/Workflow engine and therefor candidates for being an aggregate root itself.
Okey. Not the best example but it maybe gives you the idea. A root object can contain children where some of its children can also be root object because we need it in another context (than journal).
But I have myself banged my head against the wall everytime you startup with a fresh model. Just go with the flow and let the model evolve itself through its clients/subsribers. You refine the model through its usage. The Services (application services and not domain services) are of course extended with methods that respond to UI and usecases (often one-to-one).
I hope I helped you in someway...or not :D
Yes, you would most likely end up with 5 repositories (Journal, JournalEntry, Task, Problems, Notes). Your services would then use these repositories to perform CRUD for each type of entity.
Your reaction of "wow so many repositories" is not uncommon for developers new to DDD.
However, your repositories are usually light weight assuming your model and DB schema are fairly evenly matched which is often the case. If you use an ORM such as nHibernate or a tool such as codesmith generator then it gets even easier to create your repositories.
At first you need to define what is aggregate. I don't know about use case aggregates.
I know about aggregates following...
Aggregates are union of several entities. One of the entities is the aggregate root, the rest entities (or value types) have sense only in selected aggregate root context. For example you can define Order and OrderLine as an aggregate if you don't need to do any independent actions with OrderLine entities. It means that OrderLine makes sense in Order context only.
Why to define aggregates at all? It is required to reduce references between objects. That will simplify you domain model.
And of course you don't need to have OrderLineRepository if OrderLine is a part of Order aggregate.
Here is a link with more information. You can read Eric Evans DDD book. He explains aggregates very well.

Resources