File size: 4,337 Bytes
1a90b46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
```csharp
// DinnerPlanner.API (ASP.NET Core Web API)
// Domain Layer (DDD Core)
public class Recipe : Entity<Guid>
{
    public string Name { get; private set; }
    public string Description { get; private set; }
    public string Instructions { get; private set; }
    private readonly List<Ingredient> _ingredients = new();
    public IReadOnlyCollection<Ingredient> Ingredients => _ingredients.AsReadOnly();
    private readonly List<Tag> _tags = new();
    public IReadOnlyCollection<Tag> Tags => _tags.AsReadOnly();

    private Recipe() { } // For EF Core

    public Recipe(string name, string description, string instructions) 
    {
        Id = Guid.NewGuid();
        Name = name;
        Description = description;
        Instructions = instructions;
    }

    public void AddIngredient(string name)
    {
        _ingredients.Add(new Ingredient(name));
    }

    public void AddTag(string name)
    {
        _tags.Add(new Tag(name));
    }
}

public class Ingredient : ValueObject
{
    public string Name { get; }

    public Ingredient(string name)
    {
        Name = name;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Name;
    }
}

public class Tag : ValueObject
{
    public string Name { get; }

    public Tag(string name)
    {
        Name = name;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Name;
    }
}

// Application Layer (MediatR Handlers)
public class GetRecipesQuery : IRequest<List<RecipeDto>> { }

public class GetRecipesQueryHandler : IRequestHandler<GetRecipesQuery, List<RecipeDto>>
{
    private readonly IRecipeRepository _repository;

    public GetRecipesQueryHandler(IRecipeRepository repository)
    {
        _repository = repository;
    }

    public async Task<List<RecipeDto>> Handle(GetRecipesQuery request, CancellationToken cancellationToken)
    {
        var recipes = await _repository.GetAllAsync();
        return recipes.Select(r => new RecipeDto
        {
            Id = r.Id,
            Name = r.Name,
            Description = r.Description,
            Ingredients = r.Ingredients.Select(i => i.Name).ToList(),
            Instructions = r.Instructions,
            Tags = r.Tags.Select(t => t.Name).ToList()
        }).ToList();
    }
}

// Infrastructure Layer (EF Core)
public class RecipeRepository : IRecipeRepository
{
    private readonly DinnerPlannerDbContext _context;

    public RecipeRepository(DinnerPlannerDbContext context)
    {
        _context = context;
    }

    public async Task AddAsync(Recipe recipe)
    {
        await _context.Recipes.AddAsync(recipe);
        await _context.SaveChangesAsync();
    }

    public async Task<List<Recipe>> GetAllAsync()
    {
        return await _context.Recipes
            .Include(r => r.Ingredients)
            .Include(r => r.Tags)
            .ToListAsync();
    }
}

// API Layer (Controllers)
[ApiController]
[Route("api/[controller]")]
public class RecipesController : ControllerBase
{
    private readonly IMediator _mediator;

    public RecipesController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpGet]
    public async Task<ActionResult<List<RecipeDto>>> Get()
    {
        var recipes = await _mediator.Send(new GetRecipesQuery());
        return Ok(recipes);
    }

    [HttpPost]
    public async Task<ActionResult<RecipeDto>> Create(CreateRecipeCommand command)
    {
        var recipe = await _mediator.Send(command);
        return CreatedAtAction(nameof(Get), new { id = recipe.Id }, recipe);
    }
}

// Startup Configuration
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<DinnerPlannerDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddScoped<IRecipeRepository, RecipeRepository>();
        
        services.AddMediatR(typeof(GetRecipesQueryHandler));
        
        services.AddControllers();
        services.AddSwaggerGen();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints => endpoints.MapControllers());
        app.UseSwagger();
        app.UseSwaggerUI();
    }
}
```