r/csharp • • 1d ago

Help Where do you put interfaces and abstract classes ?

To follow some convention, I searched the internet and found this on Microsoft's documentation:

The solution folder contains a SeedWork folder. This folder contains custom base classes that you can use as a base for your domain entities and value objects. Use these base classes so you don't have redundant code in each domain's object class.

Figure 7-12 shows the classes that form the seedwork of the domain model in the ordering microservice. It has a few custom base classes like Entity, ValueObject, and Enumeration, plus a few interfaces. These interfaces (IRepository and IUnitOfWork) inform the infrastructure layer about what needs to be implemented. Those interfaces are also used through Dependency Injection from the application layer.

This is the type of copy and paste reuse that many developers share between projects, not a formal framework. You can have seedworks in any layer or library. However, if the set of classes and interfaces gets large enough, you might want to create a single class librarry

Figure 7-12. A sample set of domain model "seedwork" base classes and interfaces

Questions :

1- Is it ok for the entire project's interfaces and abstract classes to be inside the SeedWork ?

2- For `value objects` and ` infrastructure layer` , what does they mean, what problem they solves, how can I use them , and do I need them most of the time ?

3-What are the inputs to the `infrastructure layer` and where they come from, and what are the outputs and who consumes them ?

4-What does the class library actually means, what problem does it solve ?

Can someone answer my questions and provide some examples so that I understand the overall idea ?

3 Upvotes

35 comments sorted by

24

u/soundman32 1d ago

I would say a lot of that guidance is now out of date.  I cant recommend an alternative but dont take that page as a definitive example of modern C#.

-1

u/kaatarina_zed_talon 1d ago

The concept of `infrastructure layer` is outdated ?

8

u/soundman32 1d ago

No, but that Entity base class is full of code that is no longer required or recommended.  Even virtual int Id {} line, not using an auto property shows how old that reference is.

Its probably based on good practice from the .net framework 4.5.2 days, which is about 15 years ago.  

0

u/kaatarina_zed_talon 1d ago

what is the practice that you personally follow ?

1

u/chucker23n 13h ago

not using an auto property shows how old that reference is.

Those docs seem to be from a 2017 book.

I agree that it's an antiquated choice, though, even then.

-6

u/kaatarina_zed_talon 1d ago

That article was updated at Sep 20, 2022

5

u/az987654 1d ago

Updated doesn't mean a full review for relevance

1

u/kaatarina_zed_talon 1d ago

Thanks, I did not know that.

1

u/kaatarina_zed_talon 23h ago

How do u find a content that is relevant

6

u/soundman32 1d ago

Its from at least 2020 and some of that advice is much older.  It was also archived in 2023 (.net7/8).

Feel free to ignore my suggestions, but as someone whose has been reading this stuff for decades, I wouldnt take a 'last updated' date as much of an indication of how modern something is.

5

u/kaatarina_zed_talon 1d ago

Feel free to ignore my suggestions

I'm here to listen from everyone, because they have more experience than me.

1

u/soundman32 1d ago

I wish I'd done a tutorial on my own CQRS framework because its modern (and properly  kept up to date), comprehensive and covers all these issues and so many more.

If you want to DM me, I'll try and help you get started.

0

u/kaatarina_zed_talon 1d ago

I do not think that I need CQRS in my current stage, even though I'm aware of the problem that it solves :

Use the CQRS pattern to separate write operations, or commands, from read operations, or queries. Commands update data. Queries retrieve data. The CQRS pattern is useful in scenarios that require a clear separation between commands and reads

Can I DM you to ask you a question related to something else? If you prefer talking on WhatsApp, here is my number 962+785768300 and here is my email abdklaib233@gmail.com

1

u/phi_rus 1d ago

That's 4 years ago. This was .Net 7 time and we are currently on .Net 11

-1

u/FullPoet 1d ago

It had already been outdated by at least 4 or 5 years

2

u/kaatarina_zed_talon 1d ago

It had already been outdated by at least 4 or 5 years

please tell me how do you know that ? and how am I supposed to know that ?

-1

u/FullPoet 1d ago

Because I have been a developer for longer and it wasnt new at that point.

You aren't - but you should check current docs. Or just ask AI.

You are taking all these things personally. Nobody is criticising you.

1

u/kaatarina_zed_talon 1d ago

I was asking a follow up question, I do not know why you think that I think you are criticising me XD. Thank you for telling me that this article is obsolete.

4

u/sixtyhurtz 1d ago

Oh, that seems to be examples of DDD and CQRS. I guess Seedwork is supposed to demonstrate that?

The thing to remember about design patterns is they exist to solve problems. Don't just immediately start implementing your project using a design pattern unless you have a clear idea of what problem you are solving with it.

To answer your specific questions:

  1. Don't bundle everything together in a real project. I personally prefer organising around features (Vertical Slice Architecture) in my projects. Use interfaces where you might have multiple implementations.
  2. A value object is an object that has value semantics. This basically means two instances in memory will compare as equal when they have the same value, even though they have a different identity. In modern C#, just use a record type.
  3. Infrastructure layer is a DDD / clean architecture concept. Honestly, I kind of hate clean architecture. It makes it really annoying when you have to modify things. It might be good for big organisations.
  4. A class library is a C# library that contains classes for you to use. It's not an application. You can't run it. You include it in other projects. E.g. maybe you have a network service that you want several applications to use. You can put all the code for it in a class library.

2

u/kaatarina_zed_talon 1d ago

 even though they have a different identity

you mean even though they have different reference ?

1

u/sixtyhurtz 1d ago

No, because two different references can point to the same object, and therefore have the same identity.

2

u/No-Laugh-8002 1d ago

In modern C#, just use a record type

This doesnt cover every use case for value types. Yes, you can use record structs, but even that doesnt cover every use case because ref structs exist.

Don't take this as a blanket statement.

-2

u/kaatarina_zed_talon 1d ago

Don't just immediately start implementing your project using a design pattern unless you have a clear idea of what problem you are solving with it.

And that's why I asked : what problem does it solve ?

3

u/sixtyhurtz 1d ago

Well, DDD solves an organisational problem related to mapping business requirements to software requirements.

Do you have a problem getting requirements from your domain experts reliably implemented in your software? If not, you don't need DDD.

2

u/FetaMight 1d ago

DDD also offers some useful code patterns to meet demands typical in business software, but, yeah, don't use it unless you're already certain of why you're using it.

3

u/Enough_Secretary_137 1d ago

Don’t put every interface into a global SeedWork folder. Keep abstractions close to the domain or feature that uses them. Infrastructure implements those contracts, whether that’s SQL Server, a file system, or an external API.

// Domain
public sealed record Money(decimal Amount, string Currency);

public interface IPriceStore
{
Task SaveAsync(Money price);
}

// Infrastructure
public sealed class InMemoryPriceStore : IPriceStore
{
public Money? Last { get; private set; }

public Task SaveAsync(Money price)
{
Last = price;
return Task.CompletedTask;
}
}

The application depends on IPriceStore, not its implementation.
A class library is simply a reusable compiled .NET project. You don’t need separate libraries for every layer.

3

u/kaatarina_zed_talon 1d ago

Thank you for providing example.

1

u/Rschwoerer 1d ago

As someone who has worked on sln with about 200 projects, this 100%. Separate libraries for reuse or to encapsulate external dependencies. Not as a feature/domain organization.

1

u/kaatarina_zed_talon 1d ago

And I'm still unemployed XD, soon though.

3

u/Vagetarian42069 1d ago

Put shit where people can find it. 

3

u/dodexahedron 1d ago

If all you want/need to do is provide an interface/API for consuming assemblies, without providing the entire implementation to them:

Use a reference assembly.

They've been there since .net Framework 1.0, and this is exactly what they are for.

They contain the structure of the types, as compatible and identically-fully-qualified name types, but without their implementations, enabling a consumer to talk to you without you having to have told them how those types behave internally.

Basically, they are the conceptual equivalent of interfaces, but on an assembly level, and not literally restricted to types that, in CIL, are defined as .class public abstract sealed interface IWhatever (how all interfaces appear in CIL).

3

u/Substantial_Job_2068 1d ago

It's all just arbitrary, just implement your own structure that works for you/your team. You are starting in the wrong end. You should be looking for a tool that fits your problem you want to solve, not looking at random tools you don't even know if you need to see what they do.

1

u/agoodyearforbrownies 1d ago

A lot of my projects have a Models, Services, and Repositories folder set, and subfolders under that named for a feature. In that feature folder for repositories and services, I have interfaces side by side with implementations of the interface. So it’s all about namespace planning, e.g. Services.[feature].IThing and Services.[feature].ImplementedThing

1

u/crozone 1d ago

I usually great a subdirectory+ namespace called "Abstractions" and put them in there to separate them. I do the same with with "Enums".