The Problem
• PATCH/Opportunity updates multiple fields
like stage_id, budget, etc.
• Using nullable DTOs introduces ambiguity:
• - Is `null` an intentional update or field
omission?
• - We can't distinguish between 'not passed' vs
'set null'
3.
What We Want
•We need to distinguish three scenarios:
• 1. Omitted field → Don't update
• 2. Field set to a value → Update
• 3. Field explicitly null → Unset (set to null)
4.
Approach 1 –PatchableValue<T>
• Custom wrapper type with IsSet flag.
• Pros:
• - Strongly typed
• - No ambiguity
• - Reusable for all types
• Cons:
• - Requires custom converter
5.
PatchableValue Example
• publicstruct PatchableValue<T>
• {
• public T? Value { get; }
• public bool IsSet { get; }
• public PatchableValue(T? value) { Value =
value; IsSet = true; }
• public static readonly PatchableValue<T>
Unset = default;
• }
6.
Approach 2 –Use string in DTO
• All fields are strings in DTO. Parsed in
application layer.
• Pros:
• - Simple, no converters
• - Clean DTOs
• - Easy for frontend
• Cons:
7.
String DTO Example
•DTO:
• public string? StageId { get; set; }
• Logic:
• if (dto.StageId == "") → Set null
• if (dto.StageId != null) → Parse and set
• if (dto.StageId == null) → Don't update
8.
Comparison
• PatchableValue<T>
• +Strongly typed
• + Robust handling
• - More boilerplate
• string approach
• + Simple and lean
• + Easier for frontend
• - Weaker type safety
9.
Recommendation
• Use PatchableValue<T>for complex, scalable
needs.
• Use string-in-DTO for simple APIs or rapid
delivery.
• Standardize one approach across PATCH
endpoints.
10.
Next Steps
• Chooseone approach as standard
• Refactor existing PATCH DTOs
• Add helper methods for consistency
• Document frontend integration strategy