Autofixture: DDD 섀계λ₯Ό μœ„ν•΄ μžμ‹μ΄ λΆ€λͺ¨λ₯Ό μ°Έμ‘°ν•˜λŠ” 개체 트리 λ§Œλ“€κΈ°

에 λ§Œλ“  2014λ…„ 02μ›” 01일  Β·  14μ½”λ©˜νŠΈ  Β·  좜처: AutoFixture/AutoFixture

μ•ˆλ…•ν•˜μ„Έμš”.

μžμ‹μ΄ λΆ€λͺ¨λ₯Ό μ°Έμ‘°ν•˜λŠ” 개체 트리λ₯Ό λ§Œλ“€κ³  싢지 μ•ŠμŠ΅λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄ μžλ™μ°¨μ—λŠ” 타이어가 있고 νƒ€μ΄μ–΄λŠ” μžλ™μ°¨λ₯Ό μ°Έμ‘°ν•©λ‹ˆλ‹€.

μ§€κΈˆκΉŒμ§€ OmitOnRecursionBehaviorλ₯Ό μ‚¬μš©ν•˜κ³  μ°Έμ‘°λ₯Ό μˆ˜λ™μœΌλ‘œ ν• λ‹Ήν–ˆμŠ΅λ‹ˆλ‹€. 데λͺ¨:

``` C#
var κ³ μ • μž₯치 = new Fixture();
Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
var car = fixture.Create();
foreach (μžλ™μ°¨ νƒ€μ΄μ–΄μ˜ var.Tires)
{
타이어.μžλ™μ°¨ = μžλ™μ°¨;
}

What I wanted to do:

``` c#
fixture.Customize<Car>(composer => composer
.With(car => car.Tires, fixture.Build<Tire>().With(tire => tire.Car, car)
.CreateMany().ToList()));

λ‚΄κ°€ λ˜ν•œ ν•˜κ³  μ‹Άμ—ˆλ˜ 것:

``` C#
개인 클래슀 CarCustomization : ICustomization
{
public void μ»€μŠ€ν„°λ§ˆμ΄μ§•(IFixture μ‘°λͺ…κΈ°)
{
Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
Fixture.Customizations.Add(new CarBuilder());
}
}

개인 클래슀 CarBuilder : ISpecimenBuilder
{
곡용 객체 생성(객체 μš”μ²­, ISpecimenContext μ»¨ν…μŠ€νŠΈ)
{
var t = μœ ν˜•μœΌλ‘œ μš”μ²­;
if (t != null && t == typeof(Car))
{
//μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λΉŒλ”λŠ” μžμ²΄μ— μ˜μ‘΄ν•©λ‹ˆλ‹€.
var μžλ™μ°¨ = context.Create();
foreach (μžλ™μ°¨ νƒ€μ΄μ–΄μ˜ var.Tires)
{
타이어.μžλ™μ°¨ = μžλ™μ°¨;
}
}

    return new NoSpecimen(request);
}

}

Here are the Car/Tire classes:

``` c#
public class Car
{
    public string Name { get; set; }
    public string Description { get; set; }
    public int Doors { get; set; }
    public List<Tire> Tires { get; set; }
}

public class Tire
{
    public int Size { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public Car Car { get; set; }
}

아무도 λ‚΄κ°€ 이것을 κΉ”λ”ν•˜κ²Œ ν•΄κ²°ν•˜λ„λ‘ λ„μšΈ 수 μžˆμŠ΅λ‹ˆκΉŒ?

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

@moodmosaicμ—μ„œ μ œκ³΅ν•˜λŠ” μƒ˜ν”Œ μ½”λ“œλŠ” κ΄€μš©μ μ΄λ―€λ‘œ νŠΉμ • λ¬Έμ œμ— λŒ€ν•œ νŠΉμ • μ†”λ£¨μ…˜μœΌλ‘œ λ‚˜μ•„κ°€μ•Ό ν•©λ‹ˆλ‹€.

DDD에 ORM을 μ‚¬μš©ν•˜λŠ” 것이 μ μ ˆν•˜λ‹€λŠ” 점에 λŒ€ν•΄μ„œλŠ” λ™μ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ORM은 κ°•λ ₯ν•΄ λ³΄μ΄μ§€λ§Œ μ‹€μ œλ‘œλŠ” 맀우 μ œν•œμ μž…λ‹ˆλ‹€. 문제λ₯Ό ν•΄κ²°ν•˜λŠ” κ²ƒμ²˜λŸΌ λ³΄μ΄μ§€λ§Œ 더 λ§Žμ€ 문제λ₯Ό λ§Œλ“­λ‹ˆλ‹€.

DDD와 κ΄€λ ¨ν•˜μ—¬ 블루 λΆμ—μ„œ μ„€λͺ…ν•˜λŠ” κ°€μž₯ μ€‘μš”ν•œ μ „μˆ  νŒ¨ν„΄ 쀑 ν•˜λ‚˜λŠ” _Aggregate Root_의 κ°œλ…μž…λ‹ˆλ‹€. λ‚΄κ°€ λ³Έ λͺ¨λ“  ORM은 이 νŒ¨ν„΄μ„ λ…Έκ³¨μ μœΌλ‘œ μœ„λ°˜ν•©λ‹ˆλ‹€. μ§€μ •λœ ν…Œμ΄λΈ”μ—μ„œ μ‹œμž‘ν•˜μ—¬ ν•΄λ‹Ή ν…Œμ΄λΈ”μ˜ ν–‰ λ‘œλ“œλ₯Ό μ‹œμž‘ν•œ λ‹€μŒ λ‹€μ–‘ν•œ '탐색 속성'을 μ‚¬μš©ν•˜μ—¬ μ—°κ²°λœ ν…Œμ΄λΈ”μ—μ„œ 데이터λ₯Ό λ‘œλ“œν•  수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€. μ΄λŠ” λ£¨νŠΈκ°€ μ—†μœΌλ―€λ‘œ 데이터에 λŒ€ν•œ λͺ…ν™•ν•œ μ†Œμœ κΆŒμ΄ μ—†μŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€.

여기에 λͺ¨λ“  것이 λ³€κ²½ κ°€λŠ₯ν•˜λ‹€λŠ” 점을 μΆ”κ°€ν•˜λ©΄ 곧 μ‹œμŠ€ν…œμ„ κ°–κ²Œ 될 κ²ƒμž…λ‹ˆλ‹€. μ΄λŠ” μΆ”λ‘ ν•˜κΈ° 맀우 μ–΄λ ΅μŠ΅λ‹ˆλ‹€.

λͺ¨λ“  14 λŒ“κΈ€

Tire μ—λŠ” λΆ€λͺ¨ Car λŒ€ν•œ μ°Έμ‘°κ°€ μ—†μ–΄μ•Ό ν•œλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€. μˆœν™˜ μ°Έμ‘°λŠ” λ‚˜μœ κ²ƒμœΌλ‘œ κ°„μ£Όλ©λ‹ˆλ‹€(μ΅œμ†Œν•œ autofixture에 μ˜ν•΄).

κ·ΈλŸ¬λ‚˜ 이것은 λ˜ν•œ λ‚΄κ°€ ν•΄κ²°ν•΄μ•Ό ν•  λ¬Έμ œμž…λ‹ˆλ‹€. λ³€κ²½ν•  수 μ—†λŠ” EF λͺ¨λΈλ‘œ μž‘μ—… 쀑이며 μ œκ±°ν•  수 μ—†λŠ” μˆœν™˜ μ°Έμ‘°κ°€ μžˆμŠ΅λ‹ˆλ‹€. μ§€κΈˆμ€ autofixtureλ₯Ό ν”Όν•˜κ³  μžˆμ§€λ§Œ κ°€λŠ₯ν•œ 경우 일반적으둜 이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 데 μ•½κ°„μ˜ λ…Έλ ₯을 기울이고 μ‹ΆμŠ΅λ‹ˆλ‹€.

Domain Driven Design을 μˆ˜ν–‰ν•˜λ©΄ λŒ€λΆ€λΆ„μ˜ 논리가 도메인 계측에 있고 μžμ‹μ— λŒ€ν•œ 일뢀 λ©”μ„œλ“œλŠ” 보닀 λͺ…ν™•ν•œ κ΅¬ν˜„μ„ μœ„ν•΄ μ°Έμ‘°κ°€ ν•„μš”ν•  수 μžˆμœΌλ―€λ‘œ λ•Œλ•Œλ‘œ νŽΈλ¦¬ν•©λ‹ˆλ‹€.

Eric EvansλŠ” 그의 μ±… " Domain-Driven Design - Tackling Complexity in the Heart of Software"μ—μ„œ λ‹€μŒκ³Ό 같이 λ§ν–ˆμŠ΅λ‹ˆλ‹€.
μˆœν™˜ μ°Έμ‘°λŠ” λ…Όλ¦¬μ μœΌλ‘œ λ§Žμ€ μ˜μ—­μ— μ‘΄μž¬ν•˜λ©° λ•Œλ‘œλŠ” 섀계에도 ν•„μš”ν•˜μ§€λ§Œ μœ μ§€ 관리가 κΉŒλ‹€λ‘­μŠ΅λ‹ˆλ‹€.

λ˜ν•œ EF 및 Fluent NHibernate와 같은 인기 μžˆλŠ” 지속성 κΈ°μˆ μ„ μ‚¬μš©ν•˜λ©΄ 맀핑이 ꢌμž₯λ˜κ±°λ‚˜ μ΅œμ†Œν•œ 더 μ‰½μŠ΅λ‹ˆλ‹€.

λ”°λΌμ„œ νŽΈλ¦¬ν•œ μ†”λ£¨μ…˜μ„ μ°ΎλŠ” 것은 DDD, EF λ˜λŠ” Fluent NHibernateλ₯Ό μ‚¬μš©ν•˜λŠ” λ‚˜μ™€ λ‹€λ₯Έ μ‚¬λžŒλ“€μ—κ²Œ 도움이 될 κ²ƒμž…λ‹ˆλ‹€.

@ploeh 와 @moodmosaic 은 과거에 μˆœν™˜ μ°Έμ‘°λ₯Ό μ’‹μ•„ν•˜μ§€ μ•ŠμœΌλ©° κ°€λŠ₯ν•œ ν•œ ν”Όν•œλ‹€κ³  λΆ„λͺ…νžˆ λ°ν˜”μŠ΅λ‹ˆλ‹€. λ‚΄ 이해).

κ·ΈλŸ¬λ‚˜ λ‘˜ λ‹€ AutoFixture에 λŒ€ν•œ κΈ°μ—¬λ₯Ό μˆ˜μš©ν•˜λŠ” κ²ƒμœΌλ‘œ μ•Œλ €μ Έ μžˆμ§€λ§Œ μœ μ§€ 관리 및 지원 문제λ₯Ό μΌμœΌν‚€μ§€ μ•ŠλŠ” ν•œ νŠΉλ³„νžˆ μœ μš©ν•˜μ§€ μ•Šλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€(맀우 κ³΅μ •ν•˜λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€).

λ‚˜λŠ” 이 λ¬Έμ œμ— λŒ€ν•΄ λ­”κ°€λ₯Ό μ‹œλ„ν•˜κ³  ν•˜κ³ μž ν•˜λŠ” 동기가 맀우 ν½λ‹ˆλ‹€(일반적으둜 μž‘λ™ν•˜λŠ” μœ μš©ν•œ 것이 μžˆλ‹€κ³  κ°€μ •).

λ‚˜λŠ” 쒋은 λ””μžμΈμ„ μž₯λ €ν•˜λŠ” ν”„λ ˆμž„μ›Œν¬λ₯Ό μ’‹μ•„ν•˜κ³  μˆœν™˜ 참쑰의 문제λ₯Ό μ΄ν•΄ν•˜μ§€λ§Œ λ•Œλ‘œλŠ” 그것이 논리적인 μΌμž…λ‹ˆλ‹€. μˆœν™˜ μ°Έμ‘°λ₯Ό ꢌμž₯ν•˜μ§€ μ•Šμ§€λ§Œ AutoFixture의 아름닀움을 ν•΄μΉ˜μ§€ μ•ŠλŠ” μ†”λ£¨μ…˜μ„ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

κ·€ν•˜μ˜ λ‹΅λ³€κ³Ό 긍정적인 νƒœλ„μ— κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€ :+1:

예, _λ§Œμ•½_ μš°λ¦¬κ°€ μ μ ˆν•œ 일반 μ†”λ£¨μ…˜μ„ μ°Ύμ•„λ‚Ό 수 μžˆλ‹€λ©΄, 그것은 AutoFixture의 _default_ λ™μž‘μ΄ 아닐 κ²ƒμž…λ‹ˆλ‹€.

μœ„μ˜ CarBuilder (μž‘λ™ν•˜μ§€ μ•ŠλŠ” 것)μ—μ„œ μ‹€μ œλ‘œ ν•„μš”ν•œ 것이 Postprocessor<Car> κ²ƒμ²˜λŸΌ λ³΄μž…λ‹ˆλ‹€. 당신은 그것을 μ‹œλ„ ν–ˆμŠ΅λ‹ˆκΉŒ?

일반적으둜 ORM ν΄λž˜μŠ€μ— AutoFixtureλ₯Ό μ‚¬μš©ν•˜λ €λŠ” μš•κ΅¬λŠ” λ˜ν’€μ΄λ˜λŠ” 주제이며 @adamchesterκ°€ μ–ΈκΈ‰

λ°˜λ©΄μ— ORM을 3λ…„ 이상 μ‚¬μš©ν•˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— 이 κΈ°λŠ₯을 μΆ”κ΅¬ν•˜λŠ” 데 개인적인 관심이 μ—†κΈ° λ•Œλ¬Έμ— λ‹€λ₯Έ μ‚¬λžŒμ΄ 주도해야 ν•©λ‹ˆλ‹€.

μˆœν™˜ 참쑰의 ν•„μš”μ„±κ³Ό κ΄€λ ¨ν•˜μ—¬ λ‚˜λŠ” κ·Έ κ°œλ…μ΄ ν˜„μ‹€μ μ΄λΌλŠ” 데 λ™μ˜ν•˜μ§€λ§Œ, 그것이 μ†Œν”„νŠΈμ›¨μ–΄ λ””μžμΈμ—μ„œ 그것을 μ‚¬μš©ν•˜λŠ” 것이 쒋은 μƒκ°μ΄λΌλŠ” 것을 μ˜λ―Έν•˜μ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€. μˆœν™˜ μ°Έμ‘°λŠ” μ½”λ“œλ₯Ό μ‚¬μš©ν•˜κ³  μΆ”λ‘ ν•˜κΈ° 훨씬 더 μ–΄λ ΅κ²Œ λ§Œλ“­λ‹ˆλ‹€. ν”Όν•  수 μ—†λŠ” 상황이라면 그에 따라 μ‚΄μ•„μ•Ό ν•˜μ§€λ§Œ IMEλŠ” μˆœν™˜ μ°Έμ‘°λ₯Ό ν”Όν•˜λŠ” λ°©μ‹μœΌλ‘œ APIλ₯Ό _항상_ 섀계할 수 μžˆμŠ΅λ‹ˆλ‹€. μˆœν™˜ μ°Έμ‘°λ₯Ό μ‚¬μš©ν•˜μ—¬ APIλ₯Ό λ§ˆμ§€λ§‰μœΌλ‘œ λ§Œλ“  것이 μ–Έμ œμΈμ§€ λͺ¨λ₯΄κ² μ§€λ§Œ μˆ˜λ…„ μ „μž…λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ 이λ₯Ό ν•„μš”λ‘œ ν•˜λŠ” ORMSλŠ” μ–΄λ–»μŠ΅λ‹ˆκΉŒ?

ORM을 μ‚¬μš©ν•œλ‹€λ©΄ 더 이상 객체 지ν–₯ μ½”λ“œλ‘œ μž‘μ—…ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. κ·€ν•˜μ˜ μ½”λ“œλŠ” κ°•λ ₯ν•œ 절차적 μš”μ†Œλ₯Ό ν¬ν•¨ν•˜λŠ” _relational_이 될 κ²ƒμž…λ‹ˆλ‹€. μ–Έμ–΄ 선택(C# λ“±)에 속지 λ§ˆμ‹­μ‹œμ˜€.

μ•„λ§ˆλ„ 이 λ°©λ²•μœΌλ‘œ DDDλ₯Ό μˆ˜ν–‰ν•  수 μžˆμ§€λ§Œ 객체 지ν–₯ DDDκ°€ μ•„λ‹Œ κ΄€κ³„ν˜• DDDκ°€ 될 κ²ƒμž…λ‹ˆλ‹€. Martin Fowlerκ°€ νŠΈλžœμž­μ…˜ 슀크립트 라고 λΆ€λ₯΄λŠ” κ²ƒμœΌλ‘œ λλ‚©λ‹ˆλ‹€.

λ‹΅λ³€ κ°μ‚¬ν•©λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€ :+1:

PostProcessor에 λŒ€ν•΄ λ“€μ–΄λ³Έ 적이 μ—†μŠ΅λ‹ˆλ‹€.κ·ΈλŸ¬λ‚˜ λ‚˜λŠ” 그것을 μ‹œλ„ν–ˆκ³  그것을 μž‘λ™μ‹œν‚€μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. μ•„λ§ˆλ„ λ‚΄κ°€ λ­”κ°€λ₯Ό λ†“μΉ˜κ³ μžˆμ„ κ²ƒμž…λ‹ˆλ‹€. PostProcessorλ₯Ό μ‚¬μš©ν•˜λŠ” κ²ƒμ²˜λŸΌ μœ ν˜•μ΄ 싀행될 λ•Œ κ°’μœΌλ‘œ μ΄ˆκΈ°ν™”λ˜μ§€ μ•Šμ€ 것 κ°™μŠ΅λ‹ˆλ‹€.

λ‚΄ μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

``` C#
λ‚΄λΆ€ 클래슀 DomainCustomization : ICustomization
{
public void μ»€μŠ€ν„°λ§ˆμ΄μ§•(IFixture μ‘°λͺ…κΈ°)
{
Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
Fixture.Customizations.Add(
μƒˆλ‘œμš΄ 포슀트 ν”„λ‘œμ„Έμ„œ(((Fixture) Fixture).Engine, μƒˆλ‘œμš΄ CarCommand())
);
}
}

λ‚΄λΆ€ 클래슀 CarCommand : ISpecimenCommand
{
public void Execute(객체 ν‘œλ³Έ, ISpecimenContext μ»¨ν…μŠ€νŠΈ)
{
var car = Car둜 ν‘œλ³Έ;
if (μžλ™μ°¨ != null)
{
//μ—¬κΈ°μ„œ null μ°Έμ‘° μ˜ˆμ™Έ, 타이어 μ—†μŒ, μžλ™μ°¨ 섀정에 κ°’ μ—†μŒ
foreach (μžλ™μ°¨ νƒ€μ΄μ–΄μ˜ var.Tires)
{
타이어.μžλ™μ°¨ = μžλ™μ°¨;
}
}
}
}
```

λ§ˆν‹΄ νŒŒμšΈλŸ¬λŠ” 제 μ˜μ›… 쀑 ν•œ λͺ…μž…λ‹ˆλ‹€. 그의 κ²Œμ‹œλ¬Ό 쀑 ν•˜λ‚˜μ—μ„œ κ·ΈλŠ” μ •ν™•νžˆ λ‚΄κ°€ ν”Όν•˜λ €κ³  ν•˜λŠ” 빈혈 도메인 λͺ¨λΈμ— λŒ€ν•΄ μ΄μ•ΌκΈ°ν•©λ‹ˆλ‹€. λ‚΄ μ—”ν„°ν‹°μ—λŠ” μ„œλΉ„μŠ€(νŠΈλžœμž­μ…˜ 슀크립트)κ°€ μ•„λ‹Œ μ‘μš© ν”„λ‘œκ·Έλž¨μ— λŒ€λΆ€λΆ„μ˜ 논리가 μžˆμŠ΅λ‹ˆλ‹€. μ—”ν„°ν‹°μ—λŠ” λͺ¨λ“  논리가 μžˆμœΌλ―€λ‘œ κ΄€λ ¨ 데이터가 ν•„μš”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ‚˜λŠ” 항상 도메인 λͺ¨λΈμ΄ μ™„λ£Œλœ ν›„ 지속성 계측을 κ΅¬ν˜„ν•˜λŠ” κ²ƒμœΌλ‘œ 끝내렀고 λ…Έλ ₯ν•˜λ―€λ‘œ λ°μ΄ν„°λ² μ΄μŠ€κ°€ λ‚΄ μ—”ν‹°ν‹°λ₯Ό λ°˜μ˜ν•˜λŠ” 것이 μ•„λ‹™λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ EF/EF CodeFirst 및 NHibernate와 같은 ORMSλŠ” 섀계λ₯Ό κ΄€κ³„ν˜•μœΌλ‘œ λ§Œλ“œλŠ” μ›λž˜ 섀계에 ν•„μš”ν•œ 것보닀 더 λ§Žμ€ μˆœν™˜ 참쑰와 엔티티에 ν•„μš”ν•˜μ§€ μ•Šμ€ 관계λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

이 ORMSλŠ” 맀우 κ°•λ ₯ν•˜λ©° 이 "좔함"을 μ •λ‹Ήν™”ν•©λ‹ˆλ‹€.
λ‚˜λŠ” 이 λ¬Έμ œμ— λŒ€ν•œ 해결책이 λ‹€λ₯Έ μ‚¬λžŒλ“€μ—κ²Œ 도움이 될 수 μžˆλ„λ‘ λ‹ΉλΆ„κ°„ μ΄λŸ¬ν•œ 기술과 λ‹€λ₯Έ λ§Žμ€ 기술과 ν•¨κ»˜ μ‚΄μ•„μ•Ό ν•  κ²ƒμž…λ‹ˆλ‹€.

λ‹€μŒμ€ _Tires_κ°€ μ±„μ›Œμ§„ SUTκ°€ μƒμ„±λ˜κ³  각 Tire κ°€ Car μ°Έμ‘°ν•˜λŠ” 톡과 ν…ŒμŠ€νŠΈμž…λ‹ˆλ‹€.

``` C#
[사싀]
곡개 무효 ν…ŒμŠ€νŠΈ()
{
var κ³ μ • μž₯치 = new Fixture();
μ •μ°©λ¬Ό.μ‚¬μš©μž μ •μ˜(c => c.Without(x => x.Car));
Fixture.Customizations.Add(
μƒˆλ‘œμš΄ FilteringSpecimenBuilder(
μƒˆλ‘œμš΄ 포슀트 ν”„λ‘œμ„Έμ„œ(
μƒˆλ‘œμš΄ λ©”μ„œλ“œ 호좜자(
μƒˆλ‘œμš΄ ModestConstructorQuery()),
μƒˆλ‘œμš΄ CarFiller()),
μƒˆλ‘œμš΄ CarSpecification()));

var car = fixture.Create<Car>();

Assert.NotEmpty(car.Tires);
Array.ForEach(car.Tires.ToArray(), x => Assert.NotNull(x.Car));

}

개인 클래슀 CarFiller : ISpecimenCommand
{
public void Execute(객체 ν‘œλ³Έ, ISpecimenContext μ»¨ν…μŠ€νŠΈ)
{
if (μ‹œνŽΈ == null)
μƒˆλ‘œμš΄ ArgumentNullException("μƒ˜ν”Œ") λ˜μ§€κΈ°;
if (μ»¨ν…μŠ€νŠΈ == null)
μƒˆλ‘œμš΄ ArgumentNullException("context") λ˜μ§€κΈ°;

    var car = specimen as Car;
    if (car == null)
        throw new ArgumentException(
            "The specimen must be an instance of Car.",
            "specimen");

    Array.ForEach(car.GetType().GetProperties(), x =>
    {
        if (x.Name == "Tires")
        {
            var tires =
                ((IEnumerable<object>)context
                    .Resolve(new MultipleRequest(typeof(Tire))))
                        .Cast<Tire>().ToArray();
            Array.ForEach(tires, tire => tire.Car = car);
            x.SetValue(car, tires.ToList());
        }
        else x.SetValue(car, context.Resolve(x.PropertyType));
    });
}

}

개인 클래슀 CarSpecification : IRequestSpecification
{
public bool IsSatisfiedBy(객체 μš”μ²­)
{
var requestType = μœ ν˜•μœΌλ‘œ μš”μ²­;
if (μš”μ²­ μœ ν˜• == null)
거짓을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

    return typeof(Car).IsAssignableFrom(requestType);
}

}
```

λ‹¬μ½€ν•œ! @moodmosaic κ°μ‚¬ν•©λ‹ˆλ‹€. 이 νΌμ¦μ—μ„œ λͺ‡ μž₯의 사진이 λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
μ•„λ¦„λ‹€μš΄ μ½”λ“œ :)

λ˜ν•œ μ˜κ²¬μ„ μ£Όμ‹  @adamchester 와 @ploehμ—κ²Œλ„ κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€.

이 문제λ₯Ό μ’…λ£Œν•˜κ² μŠ΅λ‹ˆλ‹€. 이 정도면 μΆ©λΆ„ν•©λ‹ˆλ‹€. 보닀 일반적인 μ†”λ£¨μ…˜μ„ λ§Œλ“€λ €λ©΄ 이것을 λ‹€μ‹œ μ—΄ 수 μžˆμŠ΅λ‹ˆλ‹€.

@moodmosaicμ—μ„œ μ œκ³΅ν•˜λŠ” μƒ˜ν”Œ μ½”λ“œλŠ” κ΄€μš©μ μ΄λ―€λ‘œ νŠΉμ • λ¬Έμ œμ— λŒ€ν•œ νŠΉμ • μ†”λ£¨μ…˜μœΌλ‘œ λ‚˜μ•„κ°€μ•Ό ν•©λ‹ˆλ‹€.

DDD에 ORM을 μ‚¬μš©ν•˜λŠ” 것이 μ μ ˆν•˜λ‹€λŠ” 점에 λŒ€ν•΄μ„œλŠ” λ™μ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ORM은 κ°•λ ₯ν•΄ λ³΄μ΄μ§€λ§Œ μ‹€μ œλ‘œλŠ” 맀우 μ œν•œμ μž…λ‹ˆλ‹€. 문제λ₯Ό ν•΄κ²°ν•˜λŠ” κ²ƒμ²˜λŸΌ λ³΄μ΄μ§€λ§Œ 더 λ§Žμ€ 문제λ₯Ό λ§Œλ“­λ‹ˆλ‹€.

DDD와 κ΄€λ ¨ν•˜μ—¬ 블루 λΆμ—μ„œ μ„€λͺ…ν•˜λŠ” κ°€μž₯ μ€‘μš”ν•œ μ „μˆ  νŒ¨ν„΄ 쀑 ν•˜λ‚˜λŠ” _Aggregate Root_의 κ°œλ…μž…λ‹ˆλ‹€. λ‚΄κ°€ λ³Έ λͺ¨λ“  ORM은 이 νŒ¨ν„΄μ„ λ…Έκ³¨μ μœΌλ‘œ μœ„λ°˜ν•©λ‹ˆλ‹€. μ§€μ •λœ ν…Œμ΄λΈ”μ—μ„œ μ‹œμž‘ν•˜μ—¬ ν•΄λ‹Ή ν…Œμ΄λΈ”μ˜ ν–‰ λ‘œλ“œλ₯Ό μ‹œμž‘ν•œ λ‹€μŒ λ‹€μ–‘ν•œ '탐색 속성'을 μ‚¬μš©ν•˜μ—¬ μ—°κ²°λœ ν…Œμ΄λΈ”μ—μ„œ 데이터λ₯Ό λ‘œλ“œν•  수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€. μ΄λŠ” λ£¨νŠΈκ°€ μ—†μœΌλ―€λ‘œ 데이터에 λŒ€ν•œ λͺ…ν™•ν•œ μ†Œμœ κΆŒμ΄ μ—†μŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€.

여기에 λͺ¨λ“  것이 λ³€κ²½ κ°€λŠ₯ν•˜λ‹€λŠ” 점을 μΆ”κ°€ν•˜λ©΄ 곧 μ‹œμŠ€ν…œμ„ κ°–κ²Œ 될 κ²ƒμž…λ‹ˆλ‹€. μ΄λŠ” μΆ”λ‘ ν•˜κΈ° 맀우 μ–΄λ ΅μŠ΅λ‹ˆλ‹€.

ν˜„μž¬ ORM으둜 μ§„μ •ν•œ 집계 루트λ₯Ό λ§Œλ“œλŠ” 것은 λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€. 집계 λ£¨νŠΈλŠ” 데이터λ₯Ό λ³€κ²½ν•  수 μžˆλŠ” μœ μΌν•œ λ£¨νŠΈμ—¬μ•Ό ν•©λ‹ˆλ‹€...

ORM은 맀개 λ³€μˆ˜κ°€ μ—†λŠ” μƒμ„±μž, 각 속성에 λŒ€ν•œ μ„€μ •μž 및 탐색 속성 κ³΅κ°œμ™€ 같은 μ œμ•½ 쑰건을 λ§Œλ“­λ‹ˆλ‹€. 이것은 λ‚˜λ₯Ό 맀우 μ§œμ¦λ‚˜κ²Œ ν–ˆλ‹€.

ν˜„μž¬ μ ‘κ·Ό λ°©μ‹μœΌλ‘œλŠ” λ‚΄ 라이브러리의 μ‚¬μš©μžκ°€ 집계 루트λ₯Ό 톡해 데이터λ₯Ό λ³€κ²½ν•œλ‹€λŠ” 사싀을 믿을 수 μ—†μŠ΅λ‹ˆλ‹€. Aggregate Rootκ°€ 이것을 μ‹œν–‰ν•  수 μžˆλ”λΌλ„ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 직접적인 SQL μ—…λ°μ΄νŠΈλ₯Ό λ°©μ§€ν•˜μ§€ λͺ»ν•  κ²ƒμž…λ‹ˆλ‹€...

SqlServer 및 κ΄€κ³„ν˜• λ°μ΄ν„°λ² μ΄μŠ€μ— κ°‡νžŒ 것은 λ‹€λ₯Έ 방법을 μ°ΎλŠ” 데 도움이 λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. 더 λ‚˜μ€ 방법을 계속 μ°Ύμ•„μ•Ό ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  DDD둜 λ§Œλ“  μ‘μš© ν”„λ‘œκ·Έλž¨μ΄ 더 μœ μ—°ν•˜κ²Œ λ³€κ²½ν•˜κ³  μœ μ§€ 관리할 수 μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€. κ·Έ 쀑 μΌλΆ€λŠ” TDD와 κ²½ν—˜μ˜ 도움이 될 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

μƒκ°ν•΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€ :+1:

ν•„λŸ¬μ— μ˜ν•΄ μ²˜λ¦¬λ˜λŠ” μœ ν˜•μ— λŒ€ν•œ .Customize 호좜이 μžˆλŠ” 경우 ν•„λŸ¬κ°€ λ¬΄μ‹œλ˜λ„λ‘ ν•˜λŠ” κ²ƒμœΌλ‘œ λ‚˜νƒ€λ‚¬μŠ΅λ‹ˆλ‹€. 예: fixture.Customize<Car>(c => c.Without(x => x.Doors))

λ‚˜λŠ” 이것을 λ‹€μŒκ³Ό 같이 κ²°ν•©ν•˜λ €κ³  λ…Έλ ₯ν–ˆλ‹€.

var fixture = new Fixture();
            fixture.Customize<Tire>(c => c.Without(x => x.Car));
            ///fixture.Customize<Car>(c => c.Without(x => x.Doors));
            fixture.Customizations.Add(
                new FilteringSpecimenBuilder(
                    new Postprocessor(
                        SpecimenBuilderNodeFactory.CreateComposer<Car>().WithAutoProperties(!fixture.OmitAutoProperties)
                        .With(x=>x.Description, "Wee")
                        ,
                        new CarFiller()),
                    new CarSpecification()));


            var car = fixture.Create<Car>();

            Assert.Equal("Wee", car.Description);
            Assert.NotEmpty(car.Tires);
            Array.ForEach(car.Tires.ToArray(), x => Assert.NotNull(x.Car));
            Array.ForEach(car.Tires.ToArray(), x => Assert.True(x.Car == car));

λ”°λΌμ„œ μ„€λͺ…에 λŒ€ν•œ μ–΄μ„€μ…˜μ΄ μ‹€νŒ¨ν•©λ‹ˆλ‹€. μ—¬μ „νžˆ μžλ™ μƒμ„±λ˜κ³  μ‚¬μš©μž μ •μ˜λŠ” λ¬΄μ‹œλ©λ‹ˆλ‹€. λ‚˜μ˜ κΈ΄μž₯된 이해λ₯Ό λ°”νƒ•μœΌλ‘œ ν•„λŸ¬μ— μ „λ‹¬λœ μ‹œνŽΈμ΄ Composerλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒμ„±λœ κ²ƒμœΌλ‘œ μ˜ˆμƒν–ˆμ„ κ²ƒμž…λ‹ˆλ‹€.

νŽΈμ§‘ 이제 else x.SetValue(car, context.Resolve(x.PropertyType)) κ°€ λ‹€λ₯Έ 속성을 μžλ™μœΌλ‘œ μƒμ„±ν•˜κ³  μžˆμŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. 건배

더 μΌλ°˜ν™”λœ 버전은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

private class CollectionBackReferenceFiller<T, TElement> : ISpecimenCommand
            where T : class
        {
            private readonly string _collectionProperty;
            private readonly Action<TElement, T> _backReferenceSetter;

            public CollectionBackReferenceFiller(string collectionProperty, Action<TElement, T> backReferenceSetter)
            {
                _collectionProperty = collectionProperty;
                _backReferenceSetter = backReferenceSetter;
            }

            public void Execute(object specimen, ISpecimenContext context)
            {
                if (specimen == null)
                    throw new ArgumentNullException("specimen");
                if (context == null)
                    throw new ArgumentNullException("context");

                var typedSpecimen = specimen as T;
                if (typedSpecimen == null)
                    throw new ArgumentException(
                        "The specimen must be an instance of " + typeof(T).Name,
                        "specimen");

                Array.ForEach(typedSpecimen.GetType().GetProperties(), x =>
                {
                    if (x.Name == _collectionProperty)
                    {
                        var elements =
                            ((IEnumerable<object>)context
                                .Resolve(new MultipleRequest(typeof(TElement))))
                                    .Cast<TElement>().ToArray();
                        Array.ForEach(elements, e => _backReferenceSetter(e, typedSpecimen));

                        x.SetValue(typedSpecimen, elements.ToList());
                    }
                });
            }
        }

        private class TypeSpecification<T> : IRequestSpecification
        {
            public bool IsSatisfiedBy(object request)
            {
                var requestType = request as Type;
                if (requestType == null)
                    return false;

                return typeof(T).IsAssignableFrom(requestType);
            }
        }

κ·€ν•˜μ˜ 일반 μ†”λ£¨μ…˜μ€ ν›Œλ₯­ν–ˆμŠ΅λ‹ˆλ‹€!
보닀 μœ ν˜•μ΄ μ•ˆμ „ν•œ μ½”λ“œμ— λŒ€ν•΄ 속성 식을 μ‚¬μš©ν•˜λ„λ‘ μ—…λ°μ΄νŠΈν•˜κ³  일반 μ‚¬μš©μž 지정을 λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€. κ²°κ΅­ μ‚¬μš©ν•˜μ§€ μ•Šκ²Œ λ˜μ—ˆμ§€λ§Œ 이미 μž‘μ„±ν–ˆκΈ° λ•Œλ¬Έμ— κ³΅μœ ν•  μƒκ°μž…λ‹ˆλ‹€. :-)

μ˜ˆμ‹œ. CashRegisterμ—λŠ” 영수증이 μžˆλŠ” λͺ©λ‘μ΄ μžˆμŠ΅λ‹ˆλ‹€. μ˜μˆ˜μ¦μ—λŠ” CashRegister에 λŒ€ν•œ μ—­ μ°Έμ‘°κ°€ μžˆμŠ΅λ‹ˆλ‹€.

public class CashRegister : EntityBase
{
    public List<Receipt> Receipts { get; set; }
}

public class Receipt : EntityBase
{
    public CashRegister CashRegister { get; set; }
}

μ‚¬μš©μž μ •μ˜ μ‚¬μš©:
new BackReferenceCustomization<CashRegister, Receipt>(cashRegister => cashRegister.Receipts, receipt => receipt.CashRegister)

μ‚¬μš©μž μ •μ˜:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using AutoFixture;
using AutoFixture.Kernel;

public class BackReferenceCustomization<TTypeWithList, TTypeWithBackReference> : ICustomization where TTypeWithList : class
{
    private readonly Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> _collectionPropertyExpression;
    private readonly Expression<Func<TTypeWithBackReference, TTypeWithList>> _backReferencePropertyExpression;

    public BackReferenceCustomization(
        Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> collectionPropertyExpression,
        Expression<Func<TTypeWithBackReference, TTypeWithList>> backReferencePropertyExpression)
    {
        _collectionPropertyExpression = collectionPropertyExpression;
        _backReferencePropertyExpression = backReferencePropertyExpression;
    }

    public void Customize(IFixture fixture)
    {
        fixture.Customize<TTypeWithBackReference>(c => c.Without(_backReferencePropertyExpression));
        fixture.Customizations.Add(
            new FilteringSpecimenBuilder(
                new Postprocessor(
                    new MethodInvoker(new ModestConstructorQuery()),
                    new CollectionBackReferenceFiller<TTypeWithList, TTypeWithBackReference>(_collectionPropertyExpression, _backReferencePropertyExpression)
                ),
                new TypeSpecification<TTypeWithList>()
            )
        );
    }
}

public class CollectionBackReferenceFiller<TTypeWithList, TTypeWithBackReference> : ISpecimenCommand where TTypeWithList : class
{
    private readonly Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> _collectionPropertyExpression;
    private readonly Expression<Func<TTypeWithBackReference, TTypeWithList>> _backReferencePropertyExpression;

    public CollectionBackReferenceFiller(Expression<Func<TTypeWithList, List<TTypeWithBackReference>>> collectionPropertyExpression, Expression<Func<TTypeWithBackReference, TTypeWithList>> backReferencePropertyExpression)
    {
        _collectionPropertyExpression = collectionPropertyExpression;
        _backReferencePropertyExpression = backReferencePropertyExpression;
    }

    public void Execute(object specimen, ISpecimenContext context)
    {
        if (specimen == null)
            throw new ArgumentNullException(nameof(specimen));
        if (context == null)
            throw new ArgumentNullException(nameof(context));

        if (!(specimen is TTypeWithList typedSpecimen))
            throw new ArgumentException(
                "The specimen must be an instance of " + typeof(TTypeWithList).Name,
                nameof(specimen));

        var elements =
            ((IEnumerable<object>)context
                .Resolve(new MultipleRequest(typeof(TTypeWithBackReference))))
            .Cast<TTypeWithBackReference>().ToList();

        var collectionProperty = (PropertyInfo)((MemberExpression)_collectionPropertyExpression.Body).Member;
        collectionProperty.SetValue(typedSpecimen, elements);

        var backReferenceProperty = (PropertyInfo)((MemberExpression)_backReferencePropertyExpression.Body).Member;

        foreach (var element in elements)
        {
            backReferenceProperty.SetValue(element, typedSpecimen);
        }

        var otherProperties = typedSpecimen.GetType().GetProperties().Where(p => !p.Equals(collectionProperty) && p.SetMethod != null);
        foreach (var property in otherProperties)
        {
            property.SetValue(typedSpecimen, context.Resolve(property.PropertyType));
        }
    }
}

public class TypeSpecification<T> : IRequestSpecification
{
    public bool IsSatisfiedBy(object request)
    {
        var requestType = request as Type;
        if (requestType == null)
            return false;

        return typeof(T).IsAssignableFrom(requestType);
    }
}
이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰