Finish 和 Remove 都僅只是改變 Item 的 Status 而已,一起做完加快進度。
這邊我的 TodoItem 和 TodoList 的各項操作都是回傳 List 與 Item 本體,這可以自行調整想要的 Response。
syntax = "proto3";
option csharp_namespace = "Todo.Grpc";
service TodoItemGrpcService {
rpc CreateTodoItem (CreateTodoItemRequest) returns (TodoItemResponse);
rpc FinishTodoItem (FinishTodoItemRequest) returns (TodoItemResponse);
rpc RemoveTodoItem (RemoveTodoItemRequest) returns (TodoItemResponse);
}
message CreateTodoItemRequest {
string ListId = 1;
string Content = 2;
}
message FinishTodoItemRequest {
string Id = 1;
}
message RemoveTodoItemRequest {
string Id = 1;
}
message TodoItemResponse {
string Id = 1;
string ListId = 2;
string Content = 3;
string Status = 4;
string Color = 5;
}
怕一直寫一樣的東西,文章會太無趣,這邊教大家如何用 VS Code 快速產生程式碼吧!接下來的實作會附上一些圖示教學,這裡用到的 Shortcuts 都是 VS Code Default。
看到上述的 CreateTodoItemRequest
FinishTodoItemRequest
RemoveTodoItemRequest
相似到不行,就知道大概可以使用複製貼上了。
不過搭配一些 Tips,可以更加快速。
寫上 override
以後按下 space
就可以看到想要 override 的 Method。
選擇想要 override 的 Method 後就自動產生基本架構。
因為 CreateTodoItemRequest
FinishTodoItemRequest
RemoveTodoItemRequest
相似,我們直接把 Code 複製下來,並且把 TodoItemService
的 Method 改成想要的名稱跟 Payload。
到這邊就把 GrpcTodoItemService 實作完成,結果如下:
using Grpc.Core;
using Todo.Application;
namespace Todo.Grpc.Services;
public class GrpcTodoItemService : TodoItemGrpcService.TodoItemGrpcServiceBase
{
private readonly ITodoItemService _todoItemService;
public GrpcTodoItemService(ITodoItemService todoItemService)
{
this._todoItemService = todoItemService;
}
public override Task<TodoItemResponse> CreateTodoItem(CreateTodoItemRequest request, ServerCallContext context)
{
var result = _todoItemService.CreateTodoItem(new Guid(request.ListId), request.Content);
TodoItemResponse response = new()
{
Id = result.Id.ToString(),
ListId = result.ListId.ToString(),
Content = result.Content,
Status = result.Status.State.ToString(),
Color = result.Status.Color
};
return Task.FromResult(response);
}
public override Task<TodoItemResponse> FinishTodoItem(FinishTodoItemRequest request, ServerCallContext context)
{
var result = _todoItemService.FinishTodoItem(new Guid(request.Id));
TodoItemResponse response = new()
{
Id = result.Id.ToString(),
ListId = result.ListId.ToString(),
Content = result.Content,
Status = result.Status.State.ToString(),
Color = result.Status.Color
};
return Task.FromResult(response);
}
public override Task<TodoItemResponse> RemoveTodoItem(RemoveTodoItemRequest request, ServerCallContext context)
{
var result = _todoItemService.RemoveTodoItem(new Guid(request.Id));
TodoItemResponse response = new()
{
Id = result.Id.ToString(),
ListId = result.ListId.ToString(),
Content = result.Content,
Status = result.Status.State.ToString(),
Color = result.Status.Color
};
return Task.FromResult(response);
}
}
接著會發現有紅色蚯蚓提示我們 ITodoItemService 沒有這些 Methods。
這時候對著紅色蚯蚓提示按下 ctrl
+ .
來選擇建議修正,這時選擇 Generate Method。
之後就會發現 ITodoItemService 自動產生了對應的方法,因為我們是用 var 來宣告 result,所以這裡的回傳會是 object
,把它修正回來即可。
結果:
namespace Todo.Application;
public interface ITodoItemService
{
TodoItemResult CreateTodoItem(Guid listId, string content);
TodoItemResult FinishTodoItem(Guid guid);
TodoItemResult RemoveTodoItem(Guid guid);
}
接著我們會發現先前的 TodoItemService
並沒有實作新的發法產生錯誤。一樣在紅色蚯蚓的地方按下 ctrl
+ .
來選擇 Implement interface。
之後我們會看到系統自動產生好了對應的實作方法。
我們只需要把邏輯寫進去即可,結果如下:
using Todo.Domain.Aggregates;
using Todo.Domain.ValueObjects;
namespace Todo.Application;
public class TodoItemService : ITodoItemService
{
private static readonly List<TodoItem> _todoItems = new();
public TodoItemResult CreateTodoItem(Guid userId, string content)
{
var item = TodoItem.Create(content, userId);
_todoItems.Add(item);
return new TodoItemResult(item.Id.Value, item.ListId, item.Content, item.Status);
}
public TodoItemResult FinishTodoItem(Guid guid)
{
var item = _todoItems.SingleOrDefault(i => i.Id.Value == guid);
if (item == null)
throw new ArgumentException("Todo item not exists");
if (item.Status != new TodoItemStatus(Domain.ValueObjects.Enums.TodoItemState.Todo))
throw new ArgumentException("Todo item cannot be finished");
item.MarkAsFinished();
return new TodoItemResult(item.Id.Value, item.ListId, item.Content, item.Status);
}
public TodoItemResult RemoveTodoItem(Guid guid)
{
var item = _todoItems.SingleOrDefault(i => i.Id.Value == guid);
if (item == null)
throw new ArgumentException("Todo item not exists");
item.Remove();
return new TodoItemResult(item.Id.Value, item.ListId, item.Content, item.Status);
}
}
重新整理一下,就可以看到剛剛實作的 Finish 和 Remove 已經出現在這。
是不是很神奇,從開始到完成,就只花了五分鐘的時間就寫好了兩個相似的功能,這就是為什麼你隔壁的 Senior 同事常常在上班偷懶的原因。
Todo
Finished
Removed
細心的讀者看到這裡應該不難發現,我從最初 Account Service 的實作,都是由 Presentation Layer 開始實作,有很大的原因是因為我都用快捷鍵來自動產生物件和其方法,這節省了不少的開發時間。
另外有興趣的還可以安裝 Vim 套件讓這件事更便利快捷,但這需要花很多時間熟悉 Vim 的語法。
我們最後還有一個 Remove List 的功能要做,就當作業吧,沒意外應該也是 5 分鐘的事情。
那今天就先到這囉。