dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.14k stars 4.71k forks source link

Adding a module system similar to Java 9's JPMS #91670

Open CosminSontu opened 1 year ago

CosminSontu commented 1 year ago

Background and motivation

Background: Usually when I develop modular monoliths, my modules are composed of several assemblies: SomeModule.Ports.In.dll SomeModule.Adaptors.In.dll SomeModule.Core.dll SomeModule.Storage.Out.MsSql.dll SomeModule.Messaging.Out.VerneMq.dll

This is because there is no grouping concept between namespace and assembly, something like a Module that would be a physical artefact. Several modules could make up a dll. I know .NET supports multi-file and sattelite assemblies but it's not quite the same. Tooling support (ex. Visual Studio) is also important for this feature.

Motivation:

Watching a recent ASP.NET Community Standup on AOT, I understood some .NET libs are refactored to be more modular in order to help trimming and that this new pattern will be applied for more parts of the framework. I'm wondering if such feature would help making that pattern implementation seamless.

API Proposal

//Java JPMS API can be a starting point

API Usage

//JPMS API usage can be a starting point

Alternative Designs

No response

Risks

No response

jkotas commented 1 year ago

I understood some .NET libs are refactored to be more modular in order to help trimming

Refactoring to help trimming have not involved splitting assemblies into multiple modules so far.

jkotas commented 1 year ago

Java 9's JPMS

Existing .NET concepts (Nuget packages, reference assemblies, implementation assemblies, etc.) provide similar capabilities as JPMS.

Could you please explain the problem that you are trying to solve by this proposal in more details?

ghost commented 1 year ago

This issue has been marked needs-author-action and may be missing some important information.

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-meta See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation Background: Usually when I develop modular monoliths, my modules are composed of several assemblies: SomeModule.Ports.In.dll SomeModule.Adaptors.In.dll SomeModule.Core.dll SomeModule.Storage.Out.dll SomeModule.Storage.Out.MsSql.dll SomeModule.Messaging.Out.dll SomeModule.Messaging.Out.VerneMq.dll This is because there is no grouping concept between namespace and assembly, something like a Module that would be a physical artefact. Several modules could make up a dll. I know .NET supports multi-file and sattelite assemblies but it's not quite the same. Tooling support (ex. Visual Studio) is also important for this feature. Motivation: Watching a recent ASP.NET Community Standup on AOT, I understood some .NET libs are refactored to be more modular in order to help trimming and that this new pattern will be applied for more parts of the framework. I'm wondering if such feature would help making that pattern implementation seamless. ### API Proposal //Java JPMS API can be a starting point ### API Usage //JPMS API usage can be a starting point ### Alternative Designs _No response_ ### Risks _No response_
Author: CosminSontu
Assignees: -
Labels: `area-Meta`, `untriaged`, `needs-author-action`, `needs-area-label`
Milestone: -
CosminSontu commented 1 year ago

Thanks for your answer. Basically whenever I am defining a "module" in the context of a modular monolith, this "module" is composed of a group of assemblies as listed above.

I can see how Reference Assemblies could make some of those assemblies (the ones containing contracts), dissapear. My module will be composed of a cluster of assemblies in the end:

SomeModule.Ports.In.dll SomeModule.Core.dll SomeModule.Storage.Out.MsSql.dll SomeModule.Messaging.Out.VerneMq.dll

What I'm looking for, is to have the ability that my "module" is a single physical assembly: SomeModule.dll And I would like to have another physical level below the assembly (I will confusingly use the term "netmodule") since namespaces are a weak boundary from my point of view: SomeModule.dll
|

Ideally there would be project files specific to netmodules since they can have their own dependencies.

The project file corresponding to SomeModule.dll (our "module") would summarize the dependencies of included netmodules (if one excludes a netmodule, the summary would be udpated accordingly) This project file would also control visibility of contained netmodules contracts. At compile time, SomeModule.dll will be built by linking the included netmodules.

Basically, I am missing some kind physical partitioning of assemblies and the only way I can do this today in .NET is using a folder structure and a set of related projects / assemblies.

jkotas commented 1 year ago

the only way I can do this today in .NET is using a folder structure and a set of related projects / assemblies.

Yes, it is fairly common to organize source code into a folder structure for large assemblies. We do that a lot in this repo. Why is that not good enough?

CosminSontu commented 1 year ago

I'm looking for physical boundaries for separating various aspects in my modules since namespaces can be easily misused.

I guess I'll have to use nuget to encapsulate the cluster of assemblies representing a module. Also, publishing reference assemblies for module contracts will help.

There is some experimenting to do but should be good enough.

Ideally, this could be achievable with less moving parts.

Thanks

jkotas commented 1 year ago

You can setup the enforcement of internal architecture by building the same sources twice (using two sets of project files). One set of projects builds large assemblies that you ship in nuget packages. Second set of projects builds fine grained assemblies to enforce your internal architecture boundaries. (BTW: WPF was built like that in the past to enforce internal architecture layering.)

CosminSontu commented 1 year ago

I'll give it a try. Thanks!

Edit:

I managed to create a sample Module nuget package which exposes an API and consumes other two sample assemblies (one defining interfaces and another with an implementation )

my sample GreeterModule.scproj looks like this:

`

` ` __ ` ` ____net7.0 ` ` ____enable ` ` ____enable ` ` ____True ` ` ____4.0.0 ` ` ____true ` ` ____$(NoWarn),NU5118 ` ` ____ ` ` ____$(TargetsForTfmSpecificContentInPackage);AddReferencedAssemblyToPackage ` ` __ ` ` __ ` ` ____ ` ` ____ ` ` __ ` ` __ ` ` ____ ` ` ____ ` ` ______ ` ` ____ ` ` ____ ` ` ______ ` ` ____ ` ` __ ` `

`

This allows me to have a Module nuget package which can be referenced as a whole. In case some of these dependencies are common between modules, they must become nugets and referenced as PackageReferences.

I think this is what I need. I'm aware it's not recommended to pack multiple assemblies inside a nuget package and there's a pending open issue which would improve things (https://github.com/NuGet/Home/issues/3891), hence the workaround.