Couple related types together to control use in code using generics
I am trying to limit the use of types by chaining the aggregate IAggregate
, the aggregate event IDomainEvent
, and Identity
together with generics, I have snipped the below code to give context of the issue of what I have got so far.
I have the following interfaces:
public abstract class Identity<T>
{
protected abstract string GetIdentity();
}
public interface IAggregate<T>
{
Identity<T> Identity { get; }
}
public interface IDomainEvent<TIdentity,TIdentity>
where T : Identity<TIdentity>
{
TIdentity Id { get; }
}
I implement with the below:
public class TestUserId : Identity<TestUser>
{
public TestUserId(string name) { Name = name; }
readonly public string Name;
protected override string GetIdentity() => Name.ToLowerInvariant();
}
public class TestUser : IAggregate<TestUser>
{
public TestUser(TestUserId id)
{
Id = id;
var ev = new TestUserCreated()
}
public TestUserId Id { get; }
public Identity<TestUser> Identity => Id;
}
public class TestUserCreated : IDomainEvent<TestUserId, TestUser>
{
public TestUserCreated() { }
public TestUserId Id => throw new NotImplementedException();
}
Then in the command handler, for this event to be used (and for me to be able to obtain the TestUserId
which should be member of the domainEvent
object).
public interface IDomainEventHandler<TEvent>
{
void Handle(TEvent domainEvent, bool isReplay);
}
That gives me the code:
public class TesterHandler : IDomainEventHandler<TestUser, TestUserCreated>
{
public void Handle(TestUserCreated domainEvent, bool isReplay)
{
// can access the ID (of type TestUserId)
var testUserId = domainEvent.Id;
}
}
So the above TesterHandler
is fine exactly how i would want - however the compiler is failing on class TestUserCreated : IDomainEvent<TestUserId, TestUser>
with The type TestUserId' cannot be used as type parameter 'TIdentity' in the generic type or method 'IDomainEvent<TIdentity, Type>'. There is no implicit reference conversion from 'TestUserId' to 'Identity<TestUser>'.
What I want is to couple (without OO inheritance) the event to the aggregate, so that it is tied to a specific aggregate type (i.e. specific events are tied to a specific entity, and the entity ID is part of the event type as a field), I want to try and make it impossible to code event handlers for unrelated aggregates.
I am trying to implement but the compiler complains of boxing and implicit casting errors (depending on what i try/guess), in short I am unsure how to code the above.
c# generics
|
show 3 more comments
I am trying to limit the use of types by chaining the aggregate IAggregate
, the aggregate event IDomainEvent
, and Identity
together with generics, I have snipped the below code to give context of the issue of what I have got so far.
I have the following interfaces:
public abstract class Identity<T>
{
protected abstract string GetIdentity();
}
public interface IAggregate<T>
{
Identity<T> Identity { get; }
}
public interface IDomainEvent<TIdentity,TIdentity>
where T : Identity<TIdentity>
{
TIdentity Id { get; }
}
I implement with the below:
public class TestUserId : Identity<TestUser>
{
public TestUserId(string name) { Name = name; }
readonly public string Name;
protected override string GetIdentity() => Name.ToLowerInvariant();
}
public class TestUser : IAggregate<TestUser>
{
public TestUser(TestUserId id)
{
Id = id;
var ev = new TestUserCreated()
}
public TestUserId Id { get; }
public Identity<TestUser> Identity => Id;
}
public class TestUserCreated : IDomainEvent<TestUserId, TestUser>
{
public TestUserCreated() { }
public TestUserId Id => throw new NotImplementedException();
}
Then in the command handler, for this event to be used (and for me to be able to obtain the TestUserId
which should be member of the domainEvent
object).
public interface IDomainEventHandler<TEvent>
{
void Handle(TEvent domainEvent, bool isReplay);
}
That gives me the code:
public class TesterHandler : IDomainEventHandler<TestUser, TestUserCreated>
{
public void Handle(TestUserCreated domainEvent, bool isReplay)
{
// can access the ID (of type TestUserId)
var testUserId = domainEvent.Id;
}
}
So the above TesterHandler
is fine exactly how i would want - however the compiler is failing on class TestUserCreated : IDomainEvent<TestUserId, TestUser>
with The type TestUserId' cannot be used as type parameter 'TIdentity' in the generic type or method 'IDomainEvent<TIdentity, Type>'. There is no implicit reference conversion from 'TestUserId' to 'Identity<TestUser>'.
What I want is to couple (without OO inheritance) the event to the aggregate, so that it is tied to a specific aggregate type (i.e. specific events are tied to a specific entity, and the entity ID is part of the event type as a field), I want to try and make it impossible to code event handlers for unrelated aggregates.
I am trying to implement but the compiler complains of boxing and implicit casting errors (depending on what i try/guess), in short I am unsure how to code the above.
c# generics
1
Beyond anything else, you would be strongly advised to never use the name of a well-known type (such asType
) as the name of a generic parameter. I guarantee it will lead to confusion. Best practice is to name all generic parameters with a prefix ofT
similar to how interfaces are named prefixed withI
.
– Kirk Woll
Jan 1 at 19:45
1
Also, can you explain what has proven deficient with your approach? Are you receiving errors? What exactly is not working?
– Kirk Woll
Jan 1 at 19:48
well.. this usage and notion ofaggregate
is a bit of a cause of confusion to me. Rather, it has been for awhile - as It doesn't really fit the many to one operation I have. An aggregate is basically a function (event handler in your premise) that you apply to a collection (sequence of events in your premise, I think) with some sort of singular projection coming out of it (perhaps even a singular event).
– Brett Caswell
Jan 1 at 21:03
1
A few things about your code dont make sense. 1. Why doesIdentity
have a generic parameter but not use it? 2.IDomainEvent
specifies two generic parameters (named the same, must be a mistake?) and then has a generic constraint on a generic parameter which doesnt exist (T
) 3.IDomainEventHandler
specifies 1 generic parameter but is implemented with 2!
– Jamiec
Jan 2 at 8:46
1
Or, to put it another way, here is a Minimal, Complete, and Verifiable example of your code edited to actually compile and run: rextester.com/XABFSP58589 - what is wrong with this code that doesnt do what you expect?
– Jamiec
Jan 2 at 10:09
|
show 3 more comments
I am trying to limit the use of types by chaining the aggregate IAggregate
, the aggregate event IDomainEvent
, and Identity
together with generics, I have snipped the below code to give context of the issue of what I have got so far.
I have the following interfaces:
public abstract class Identity<T>
{
protected abstract string GetIdentity();
}
public interface IAggregate<T>
{
Identity<T> Identity { get; }
}
public interface IDomainEvent<TIdentity,TIdentity>
where T : Identity<TIdentity>
{
TIdentity Id { get; }
}
I implement with the below:
public class TestUserId : Identity<TestUser>
{
public TestUserId(string name) { Name = name; }
readonly public string Name;
protected override string GetIdentity() => Name.ToLowerInvariant();
}
public class TestUser : IAggregate<TestUser>
{
public TestUser(TestUserId id)
{
Id = id;
var ev = new TestUserCreated()
}
public TestUserId Id { get; }
public Identity<TestUser> Identity => Id;
}
public class TestUserCreated : IDomainEvent<TestUserId, TestUser>
{
public TestUserCreated() { }
public TestUserId Id => throw new NotImplementedException();
}
Then in the command handler, for this event to be used (and for me to be able to obtain the TestUserId
which should be member of the domainEvent
object).
public interface IDomainEventHandler<TEvent>
{
void Handle(TEvent domainEvent, bool isReplay);
}
That gives me the code:
public class TesterHandler : IDomainEventHandler<TestUser, TestUserCreated>
{
public void Handle(TestUserCreated domainEvent, bool isReplay)
{
// can access the ID (of type TestUserId)
var testUserId = domainEvent.Id;
}
}
So the above TesterHandler
is fine exactly how i would want - however the compiler is failing on class TestUserCreated : IDomainEvent<TestUserId, TestUser>
with The type TestUserId' cannot be used as type parameter 'TIdentity' in the generic type or method 'IDomainEvent<TIdentity, Type>'. There is no implicit reference conversion from 'TestUserId' to 'Identity<TestUser>'.
What I want is to couple (without OO inheritance) the event to the aggregate, so that it is tied to a specific aggregate type (i.e. specific events are tied to a specific entity, and the entity ID is part of the event type as a field), I want to try and make it impossible to code event handlers for unrelated aggregates.
I am trying to implement but the compiler complains of boxing and implicit casting errors (depending on what i try/guess), in short I am unsure how to code the above.
c# generics
I am trying to limit the use of types by chaining the aggregate IAggregate
, the aggregate event IDomainEvent
, and Identity
together with generics, I have snipped the below code to give context of the issue of what I have got so far.
I have the following interfaces:
public abstract class Identity<T>
{
protected abstract string GetIdentity();
}
public interface IAggregate<T>
{
Identity<T> Identity { get; }
}
public interface IDomainEvent<TIdentity,TIdentity>
where T : Identity<TIdentity>
{
TIdentity Id { get; }
}
I implement with the below:
public class TestUserId : Identity<TestUser>
{
public TestUserId(string name) { Name = name; }
readonly public string Name;
protected override string GetIdentity() => Name.ToLowerInvariant();
}
public class TestUser : IAggregate<TestUser>
{
public TestUser(TestUserId id)
{
Id = id;
var ev = new TestUserCreated()
}
public TestUserId Id { get; }
public Identity<TestUser> Identity => Id;
}
public class TestUserCreated : IDomainEvent<TestUserId, TestUser>
{
public TestUserCreated() { }
public TestUserId Id => throw new NotImplementedException();
}
Then in the command handler, for this event to be used (and for me to be able to obtain the TestUserId
which should be member of the domainEvent
object).
public interface IDomainEventHandler<TEvent>
{
void Handle(TEvent domainEvent, bool isReplay);
}
That gives me the code:
public class TesterHandler : IDomainEventHandler<TestUser, TestUserCreated>
{
public void Handle(TestUserCreated domainEvent, bool isReplay)
{
// can access the ID (of type TestUserId)
var testUserId = domainEvent.Id;
}
}
So the above TesterHandler
is fine exactly how i would want - however the compiler is failing on class TestUserCreated : IDomainEvent<TestUserId, TestUser>
with The type TestUserId' cannot be used as type parameter 'TIdentity' in the generic type or method 'IDomainEvent<TIdentity, Type>'. There is no implicit reference conversion from 'TestUserId' to 'Identity<TestUser>'.
What I want is to couple (without OO inheritance) the event to the aggregate, so that it is tied to a specific aggregate type (i.e. specific events are tied to a specific entity, and the entity ID is part of the event type as a field), I want to try and make it impossible to code event handlers for unrelated aggregates.
I am trying to implement but the compiler complains of boxing and implicit casting errors (depending on what i try/guess), in short I am unsure how to code the above.
c# generics
c# generics
edited Jan 2 at 18:47
g18c
asked Jan 1 at 19:39
g18cg18c
53562574
53562574
1
Beyond anything else, you would be strongly advised to never use the name of a well-known type (such asType
) as the name of a generic parameter. I guarantee it will lead to confusion. Best practice is to name all generic parameters with a prefix ofT
similar to how interfaces are named prefixed withI
.
– Kirk Woll
Jan 1 at 19:45
1
Also, can you explain what has proven deficient with your approach? Are you receiving errors? What exactly is not working?
– Kirk Woll
Jan 1 at 19:48
well.. this usage and notion ofaggregate
is a bit of a cause of confusion to me. Rather, it has been for awhile - as It doesn't really fit the many to one operation I have. An aggregate is basically a function (event handler in your premise) that you apply to a collection (sequence of events in your premise, I think) with some sort of singular projection coming out of it (perhaps even a singular event).
– Brett Caswell
Jan 1 at 21:03
1
A few things about your code dont make sense. 1. Why doesIdentity
have a generic parameter but not use it? 2.IDomainEvent
specifies two generic parameters (named the same, must be a mistake?) and then has a generic constraint on a generic parameter which doesnt exist (T
) 3.IDomainEventHandler
specifies 1 generic parameter but is implemented with 2!
– Jamiec
Jan 2 at 8:46
1
Or, to put it another way, here is a Minimal, Complete, and Verifiable example of your code edited to actually compile and run: rextester.com/XABFSP58589 - what is wrong with this code that doesnt do what you expect?
– Jamiec
Jan 2 at 10:09
|
show 3 more comments
1
Beyond anything else, you would be strongly advised to never use the name of a well-known type (such asType
) as the name of a generic parameter. I guarantee it will lead to confusion. Best practice is to name all generic parameters with a prefix ofT
similar to how interfaces are named prefixed withI
.
– Kirk Woll
Jan 1 at 19:45
1
Also, can you explain what has proven deficient with your approach? Are you receiving errors? What exactly is not working?
– Kirk Woll
Jan 1 at 19:48
well.. this usage and notion ofaggregate
is a bit of a cause of confusion to me. Rather, it has been for awhile - as It doesn't really fit the many to one operation I have. An aggregate is basically a function (event handler in your premise) that you apply to a collection (sequence of events in your premise, I think) with some sort of singular projection coming out of it (perhaps even a singular event).
– Brett Caswell
Jan 1 at 21:03
1
A few things about your code dont make sense. 1. Why doesIdentity
have a generic parameter but not use it? 2.IDomainEvent
specifies two generic parameters (named the same, must be a mistake?) and then has a generic constraint on a generic parameter which doesnt exist (T
) 3.IDomainEventHandler
specifies 1 generic parameter but is implemented with 2!
– Jamiec
Jan 2 at 8:46
1
Or, to put it another way, here is a Minimal, Complete, and Verifiable example of your code edited to actually compile and run: rextester.com/XABFSP58589 - what is wrong with this code that doesnt do what you expect?
– Jamiec
Jan 2 at 10:09
1
1
Beyond anything else, you would be strongly advised to never use the name of a well-known type (such as
Type
) as the name of a generic parameter. I guarantee it will lead to confusion. Best practice is to name all generic parameters with a prefix of T
similar to how interfaces are named prefixed with I
.– Kirk Woll
Jan 1 at 19:45
Beyond anything else, you would be strongly advised to never use the name of a well-known type (such as
Type
) as the name of a generic parameter. I guarantee it will lead to confusion. Best practice is to name all generic parameters with a prefix of T
similar to how interfaces are named prefixed with I
.– Kirk Woll
Jan 1 at 19:45
1
1
Also, can you explain what has proven deficient with your approach? Are you receiving errors? What exactly is not working?
– Kirk Woll
Jan 1 at 19:48
Also, can you explain what has proven deficient with your approach? Are you receiving errors? What exactly is not working?
– Kirk Woll
Jan 1 at 19:48
well.. this usage and notion of
aggregate
is a bit of a cause of confusion to me. Rather, it has been for awhile - as It doesn't really fit the many to one operation I have. An aggregate is basically a function (event handler in your premise) that you apply to a collection (sequence of events in your premise, I think) with some sort of singular projection coming out of it (perhaps even a singular event).– Brett Caswell
Jan 1 at 21:03
well.. this usage and notion of
aggregate
is a bit of a cause of confusion to me. Rather, it has been for awhile - as It doesn't really fit the many to one operation I have. An aggregate is basically a function (event handler in your premise) that you apply to a collection (sequence of events in your premise, I think) with some sort of singular projection coming out of it (perhaps even a singular event).– Brett Caswell
Jan 1 at 21:03
1
1
A few things about your code dont make sense. 1. Why does
Identity
have a generic parameter but not use it? 2. IDomainEvent
specifies two generic parameters (named the same, must be a mistake?) and then has a generic constraint on a generic parameter which doesnt exist (T
) 3. IDomainEventHandler
specifies 1 generic parameter but is implemented with 2!– Jamiec
Jan 2 at 8:46
A few things about your code dont make sense. 1. Why does
Identity
have a generic parameter but not use it? 2. IDomainEvent
specifies two generic parameters (named the same, must be a mistake?) and then has a generic constraint on a generic parameter which doesnt exist (T
) 3. IDomainEventHandler
specifies 1 generic parameter but is implemented with 2!– Jamiec
Jan 2 at 8:46
1
1
Or, to put it another way, here is a Minimal, Complete, and Verifiable example of your code edited to actually compile and run: rextester.com/XABFSP58589 - what is wrong with this code that doesnt do what you expect?
– Jamiec
Jan 2 at 10:09
Or, to put it another way, here is a Minimal, Complete, and Verifiable example of your code edited to actually compile and run: rextester.com/XABFSP58589 - what is wrong with this code that doesnt do what you expect?
– Jamiec
Jan 2 at 10:09
|
show 3 more comments
1 Answer
1
active
oldest
votes
Given I was unable to create running code as per comments requested (hence the reason for the post) and general complexity, I decided using generics in this way was a bad idea with rationale below.
I currently have code which calls the handler as follows (and this is working fine) passing in the sourceIdentity external to the domainEvent object:
public interface IDomainEventHandler<TIdentity, TEvent>
where TIdentity : IIdentity
where TEvent : IDomainEvent
{
void Handle(TIdentity sourceIdentity, TEvent domainEvent, bool isReplay);
}
I am passing in the aggregate ID external to the IDomainEvent object (and this is desired to keep the events, from an event sourcing perspective, as simple as possible as simple POCO objects without inheritance or involving any framework).
The reason for the question was I just wanted to explore all options with generics (so the domainEvent object could have an interface that would give an ID field) but it started to get complicated quickly, specifically additional template parameters would be required since we are inferring relationships via templates, rather than OO relationships.
Without OO, the relationship would need to be defined somewhere by adding additional types to templates to tie them together interface IDomainEvent<TIdentity,TAggregate,TEvent>
and interface IDomainEventHandler<TIdentity, TAggregate, TEvent>
, in this case OO inheritance would be preferred and result in way less code.
All this was done to give an interface to obtain the ID, however as if an ID is really needed it can be incorporated in the event as a normal field (without the need for complex OO relationships or templates).
public interface IDomainEvent
{
DateTime OccurredOn { get; set; }
Guid MessageId { get; set; }
}
public class TestUserCreated : IDomainEvent
{
// id can be accessed by whatever needs it by being
// defined explicity within the domain event POCO
// without needing any base class or framework.
public readonly TestUserId Id;
public readonly string Name;
public TestUserCreated(TestUserId id, string name)
{
Id = id;
Name = name;
}
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53998389%2fcouple-related-types-together-to-control-use-in-code-using-generics%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Given I was unable to create running code as per comments requested (hence the reason for the post) and general complexity, I decided using generics in this way was a bad idea with rationale below.
I currently have code which calls the handler as follows (and this is working fine) passing in the sourceIdentity external to the domainEvent object:
public interface IDomainEventHandler<TIdentity, TEvent>
where TIdentity : IIdentity
where TEvent : IDomainEvent
{
void Handle(TIdentity sourceIdentity, TEvent domainEvent, bool isReplay);
}
I am passing in the aggregate ID external to the IDomainEvent object (and this is desired to keep the events, from an event sourcing perspective, as simple as possible as simple POCO objects without inheritance or involving any framework).
The reason for the question was I just wanted to explore all options with generics (so the domainEvent object could have an interface that would give an ID field) but it started to get complicated quickly, specifically additional template parameters would be required since we are inferring relationships via templates, rather than OO relationships.
Without OO, the relationship would need to be defined somewhere by adding additional types to templates to tie them together interface IDomainEvent<TIdentity,TAggregate,TEvent>
and interface IDomainEventHandler<TIdentity, TAggregate, TEvent>
, in this case OO inheritance would be preferred and result in way less code.
All this was done to give an interface to obtain the ID, however as if an ID is really needed it can be incorporated in the event as a normal field (without the need for complex OO relationships or templates).
public interface IDomainEvent
{
DateTime OccurredOn { get; set; }
Guid MessageId { get; set; }
}
public class TestUserCreated : IDomainEvent
{
// id can be accessed by whatever needs it by being
// defined explicity within the domain event POCO
// without needing any base class or framework.
public readonly TestUserId Id;
public readonly string Name;
public TestUserCreated(TestUserId id, string name)
{
Id = id;
Name = name;
}
}
add a comment |
Given I was unable to create running code as per comments requested (hence the reason for the post) and general complexity, I decided using generics in this way was a bad idea with rationale below.
I currently have code which calls the handler as follows (and this is working fine) passing in the sourceIdentity external to the domainEvent object:
public interface IDomainEventHandler<TIdentity, TEvent>
where TIdentity : IIdentity
where TEvent : IDomainEvent
{
void Handle(TIdentity sourceIdentity, TEvent domainEvent, bool isReplay);
}
I am passing in the aggregate ID external to the IDomainEvent object (and this is desired to keep the events, from an event sourcing perspective, as simple as possible as simple POCO objects without inheritance or involving any framework).
The reason for the question was I just wanted to explore all options with generics (so the domainEvent object could have an interface that would give an ID field) but it started to get complicated quickly, specifically additional template parameters would be required since we are inferring relationships via templates, rather than OO relationships.
Without OO, the relationship would need to be defined somewhere by adding additional types to templates to tie them together interface IDomainEvent<TIdentity,TAggregate,TEvent>
and interface IDomainEventHandler<TIdentity, TAggregate, TEvent>
, in this case OO inheritance would be preferred and result in way less code.
All this was done to give an interface to obtain the ID, however as if an ID is really needed it can be incorporated in the event as a normal field (without the need for complex OO relationships or templates).
public interface IDomainEvent
{
DateTime OccurredOn { get; set; }
Guid MessageId { get; set; }
}
public class TestUserCreated : IDomainEvent
{
// id can be accessed by whatever needs it by being
// defined explicity within the domain event POCO
// without needing any base class or framework.
public readonly TestUserId Id;
public readonly string Name;
public TestUserCreated(TestUserId id, string name)
{
Id = id;
Name = name;
}
}
add a comment |
Given I was unable to create running code as per comments requested (hence the reason for the post) and general complexity, I decided using generics in this way was a bad idea with rationale below.
I currently have code which calls the handler as follows (and this is working fine) passing in the sourceIdentity external to the domainEvent object:
public interface IDomainEventHandler<TIdentity, TEvent>
where TIdentity : IIdentity
where TEvent : IDomainEvent
{
void Handle(TIdentity sourceIdentity, TEvent domainEvent, bool isReplay);
}
I am passing in the aggregate ID external to the IDomainEvent object (and this is desired to keep the events, from an event sourcing perspective, as simple as possible as simple POCO objects without inheritance or involving any framework).
The reason for the question was I just wanted to explore all options with generics (so the domainEvent object could have an interface that would give an ID field) but it started to get complicated quickly, specifically additional template parameters would be required since we are inferring relationships via templates, rather than OO relationships.
Without OO, the relationship would need to be defined somewhere by adding additional types to templates to tie them together interface IDomainEvent<TIdentity,TAggregate,TEvent>
and interface IDomainEventHandler<TIdentity, TAggregate, TEvent>
, in this case OO inheritance would be preferred and result in way less code.
All this was done to give an interface to obtain the ID, however as if an ID is really needed it can be incorporated in the event as a normal field (without the need for complex OO relationships or templates).
public interface IDomainEvent
{
DateTime OccurredOn { get; set; }
Guid MessageId { get; set; }
}
public class TestUserCreated : IDomainEvent
{
// id can be accessed by whatever needs it by being
// defined explicity within the domain event POCO
// without needing any base class or framework.
public readonly TestUserId Id;
public readonly string Name;
public TestUserCreated(TestUserId id, string name)
{
Id = id;
Name = name;
}
}
Given I was unable to create running code as per comments requested (hence the reason for the post) and general complexity, I decided using generics in this way was a bad idea with rationale below.
I currently have code which calls the handler as follows (and this is working fine) passing in the sourceIdentity external to the domainEvent object:
public interface IDomainEventHandler<TIdentity, TEvent>
where TIdentity : IIdentity
where TEvent : IDomainEvent
{
void Handle(TIdentity sourceIdentity, TEvent domainEvent, bool isReplay);
}
I am passing in the aggregate ID external to the IDomainEvent object (and this is desired to keep the events, from an event sourcing perspective, as simple as possible as simple POCO objects without inheritance or involving any framework).
The reason for the question was I just wanted to explore all options with generics (so the domainEvent object could have an interface that would give an ID field) but it started to get complicated quickly, specifically additional template parameters would be required since we are inferring relationships via templates, rather than OO relationships.
Without OO, the relationship would need to be defined somewhere by adding additional types to templates to tie them together interface IDomainEvent<TIdentity,TAggregate,TEvent>
and interface IDomainEventHandler<TIdentity, TAggregate, TEvent>
, in this case OO inheritance would be preferred and result in way less code.
All this was done to give an interface to obtain the ID, however as if an ID is really needed it can be incorporated in the event as a normal field (without the need for complex OO relationships or templates).
public interface IDomainEvent
{
DateTime OccurredOn { get; set; }
Guid MessageId { get; set; }
}
public class TestUserCreated : IDomainEvent
{
// id can be accessed by whatever needs it by being
// defined explicity within the domain event POCO
// without needing any base class or framework.
public readonly TestUserId Id;
public readonly string Name;
public TestUserCreated(TestUserId id, string name)
{
Id = id;
Name = name;
}
}
answered Jan 2 at 18:57
g18cg18c
53562574
53562574
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53998389%2fcouple-related-types-together-to-control-use-in-code-using-generics%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Beyond anything else, you would be strongly advised to never use the name of a well-known type (such as
Type
) as the name of a generic parameter. I guarantee it will lead to confusion. Best practice is to name all generic parameters with a prefix ofT
similar to how interfaces are named prefixed withI
.– Kirk Woll
Jan 1 at 19:45
1
Also, can you explain what has proven deficient with your approach? Are you receiving errors? What exactly is not working?
– Kirk Woll
Jan 1 at 19:48
well.. this usage and notion of
aggregate
is a bit of a cause of confusion to me. Rather, it has been for awhile - as It doesn't really fit the many to one operation I have. An aggregate is basically a function (event handler in your premise) that you apply to a collection (sequence of events in your premise, I think) with some sort of singular projection coming out of it (perhaps even a singular event).– Brett Caswell
Jan 1 at 21:03
1
A few things about your code dont make sense. 1. Why does
Identity
have a generic parameter but not use it? 2.IDomainEvent
specifies two generic parameters (named the same, must be a mistake?) and then has a generic constraint on a generic parameter which doesnt exist (T
) 3.IDomainEventHandler
specifies 1 generic parameter but is implemented with 2!– Jamiec
Jan 2 at 8:46
1
Or, to put it another way, here is a Minimal, Complete, and Verifiable example of your code edited to actually compile and run: rextester.com/XABFSP58589 - what is wrong with this code that doesnt do what you expect?
– Jamiec
Jan 2 at 10:09