DDD/整洁架构下REST服务Task类双JSON序列化方法的设计与测试咨询
关于DDD/整洁架构下Task类序列化的问题
先明确你的场景:你定义了一个领域类Task,现在需要两种JSON序列化逻辑:
- 一种用于MongoDB的持久化,输出原始路径格式的JSON
- 另一种用于REST API响应,输出拼接了服务地址的完整URL格式的JSON
@attr.s class Task: id: str = attr.ib() url: str = attr.ib() images_paths: Optional[List[str]] = attr.ib(default=None) text_path: Optional[str] = attr.ib(default=None)
下面针对你的三个疑问逐一解答:
1. 这两种序列化方法是否应放在基础设施层?从DDD视角来看是否合理?
完全不应该把这两个序列化逻辑放在Task领域类里,按照DDD和整洁架构的原则,领域层应该保持绝对纯净,只包含核心业务逻辑,不依赖任何外部技术细节(比如持久化、API格式)。
正确的做法是:
- 用于MongoDB的序列化:属于持久化细节,应该放在基础设施层(比如和MongoDB仓库实现类放在一起,或者单独的MongoDB序列化工具类)。这部分是领域对象和数据库存储格式之间的转换,属于外部依赖的适配。
- 用于REST API的序列化:属于API适配层的逻辑,应该放在接口适配器层(比如API控制器所在的模块,或者专门的DTO转换类)。这部分是领域对象到API响应格式的转换,服务于对外暴露的接口协议。
这样设计的好处是:领域层不依赖任何外部系统,当你更换数据库(比如从MongoDB换成PostgreSQL)或者修改API响应格式时,完全不需要改动Task类本身,符合依赖倒置原则。
2. 二者均为JSON序列化方法,该如何命名?
命名的核心是明确上下文和用途,避免模糊的通用名称,推荐这些命名方式:
- MongoDB序列化:
serialize_to_mongo_document、to_mongo_json、as_mongo_dict——直接体现是给MongoDB存储用的 - REST API序列化:
serialize_to_api_response、to_api_dto、as_api_response_dict——明确是用于API输出的DTO转换
举个例子,基础设施层里的MongoDB序列化方法可以这样写:
def serialize_task_to_mongo(task: Task) -> dict: return attr.asdict(task)
API层的序列化方法:
def serialize_task_to_api(task: Task, base_url: str) -> dict: task_dict = attr.asdict(task) if task_dict["images_paths"]: task_dict["images_paths"] = [ f"{base_url}/tasks/{task.id}/images/{os.path.basename(path)}" for path in task_dict["images_paths"] ] if task_dict["text_path"]: task_dict["text_path"] = f"{base_url}/tasks/{task.id}/text/{os.path.basename(task_dict['text_path'])}" return task_dict
3. 常规序列化测试无法用于REST API序列化场景,该如何进行测试?
REST API的序列化是单向转换(从领域对象到API响应,一般不需要从API响应反序列化回领域对象,因为API输入会用专门的请求DTO),所以不能用双向相等断言,而是要针对输出内容做针对性测试:
测试思路:
- 构造一个已知状态的
Task实例 - 调用API序列化方法,得到响应字典
- 逐一断言响应字典中的每个字段是否符合预期格式和值
示例测试代码(用pytest):
def test_serialize_task_to_api(): test_task = Task( id="1", url="aaa.com", images_paths=["img.png"], text_path="text.txt" ) base_url = "localhost" api_response = serialize_task_to_api(test_task, base_url) # 断言核心字段一致 assert api_response["id"] == test_task.id assert api_response["url"] == test_task.url # 断言路径被正确拼接 assert api_response["images_paths"] == ["localhost/tasks/1/images/img.png"] assert api_response["text_path"] == "localhost/tasks/1/text/text.txt" # 可选:测试空值场景 empty_task = Task(id="2", url="bbb.com") empty_response = serialize_task_to_api(empty_task, base_url) assert empty_response["images_paths"] is None assert empty_response["text_path"] is None
如果你的API有明确的JSON Schema,还可以用jsonschema库验证响应是否符合Schema定义,确保格式的一致性。
内容的提问来源于stack exchange,提问作者Mateusz
相关产品推荐
相关产品推荐

