feat: Add HaveAnyParameters and NotHaveAnyParameters
Description
This PR adds support for checking if classes have private parameterless constructors in ArchUnitNET. This feature is particularly valuable for enforcing Domain-Driven Design patterns where entities should have private parameterless constructors for ORM frameworks while maintaining public constructors with parameters for domain logic.
Motivation
In Domain-Driven Design and Entity Framework scenarios, it's common to require:
- Private parameterless constructors for ORM frameworks (Entity Framework, NHibernate, etc.)
- Public parameterized constructors for domain logic and business rules
- Abstract classes excluded from constructor requirements
This pattern ensures that:
- ORM frameworks can instantiate entities during deserialization
- Domain logic enforces proper entity creation through meaningful constructors
- Encapsulation and invariants are maintained
Implementation
Core Changes
- ✅ Added
HavePrivateParameterlessConstructor()condition usingSimpleCondition<Class> - ✅ Added
NotHavePrivateParameterlessConstructor()condition for inverse validation - ✅ Integrated into fluent API via
IClassConditionsinterface andClassConditionsDefinition - ✅ Proper nullable handling for
IsAbstractproperty (cls.IsAbstract == true) - ✅ Abstract class exclusion - automatically satisfy both conditions
Test Coverage
- ✅ Comprehensive unit tests covering all scenarios
- ✅ Edge case handling (abstract classes, parameterized-only constructors)
Usage Examples
Basic Usage
// Ensure domain entities have private parameterless constructors
var rule = Classes()
.That().ResideInNamespace("MyApp.Domain.Entities")
.And().AreNotAbstract()
.Should().HavePrivateParameterlessConstructor()
.Because("Domain entities need private constructors for ORM frameworks");
rule.Check(architecture);
Hi @Redestros, I think this is too specific to be included as a separate fluent syntax method, because it basically combines a predicate and two conditions. I think a syntax like the following would be a better way to allow for this:
var classes = Classes()
.That().ResideInNamespace("MyApp.Domain.Entities")
.And().AreNotAbstract();
MethodMembers()
.That()
.AreDeclaredIn(classes)
.And()
.AreConstructors()
.Should()
.BePrivate()
.AndShould()
.NotHaveAnyParameters();
The only function that would be missing for this is NotHaveAnyParameters. I would be happy to accept a PR for adding HaveAnyParameters and NotHaveAnyParameters.
Hi @alexanderlinne , Thank you for the feedback! You're absolutely right - the compositional approach is much more flexible. I've updated the PR accordingly. Changes Made I've removed the specific HavePrivateParameterlessConstructor condition and instead implemented the granular HaveAnyParameters and NotHaveAnyParameters conditions as you suggested. New Implementation:
✅ Added HaveAnyParameters() condition for MethodMembers ✅ Added NotHaveAnyParameters() condition for MethodMembers ✅ Added method signatures to IMethodMemberConditions interface ✅ Implemented in MethodMemberConditionsDefinition fluent API ✅ Added comprehensive test coverage
Usage Example Your suggested syntax now works perfectly:
var classes = Classes()
.That().ResideInNamespace("MyApp.Domain.Entities")
.And().AreNotAbstract();
MethodMembers()
.That()
.AreDeclaredIn(classes)
.And()
.AreConstructors()
.Should()
.BePrivate()
.AndShould()
.NotHaveAnyParameters();
Codecov Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 73.87%. Comparing base (cb83e98) to head (db5b3ca).
Additional details and impacted files
@@ Coverage Diff @@
## main #403 +/- ##
==========================================
+ Coverage 73.79% 73.87% +0.08%
==========================================
Files 259 259
Lines 16387 16409 +22
Branches 1336 1336
==========================================
+ Hits 12092 12122 +30
+ Misses 3878 3870 -8
Partials 417 417
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.