You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Bloc+Freezed连接领域层Entity与数据层Model?

关于Clean Feature-First架构中Bloc+Freezed的Entity与DTO实现疑问

我正在学习用Bloc和Freezed遵循Clean Feature-First架构。目前理解是:领域层(Domain layer)的Entity类只声明属性,供表示层使用;数据层(Data layer)的DTO类负责实现FromJson方法,以及从数据源获取数据并解析为Entity的相关逻辑。

我的疑问如下:

  1. 应该为Entity和DTO中的哪一个(或两者都)编写Freezed代码?
  2. 使用Repository时,如何连接Entity与Model?是否需要创建fromEntity、toEntity这类方法?如果需要,能否通过Freezed实现?

我当前的NoteEntity类代码:

class NoteEntity{
  final String title;
  final String description;

  NoteEntity({required this.title, required this.description});
}

用Freezed实现的版本:

@freezed
class NoteEntity with _$NoteEntity {
      const factory NoteEntity({
        required String title,
        required String description,
      }) = _NoteEntity;
}

解答

1. 该为哪类编写Freezed代码?

两者都可以用Freezed实现,各自侧重点不同:

  • Entity(领域层):用Freezed能快速生成不可变类、equals/hashCode/toString方法,还能方便扩展union类型(后续有需求时)。你当前的Freezed实现完全合理,领域层的Entity只需保持纯粹的属性定义,Freezed既省去样板代码,又能保证不可变性——这在Bloc状态管理中至关重要,避免状态被意外修改。
  • DTO(数据层):同样适合用Freezed,尤其是配合freezed_annotation和json_serializable,可以自动生成fromJson/toJson方法,无需手动编写解析逻辑。比如给DTO添加@JsonSerializable()注解,结合Freezed就能一键完成序列化/反序列化,大幅减少代码量。

DTO的Freezed实现示例:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'note_dto.freezed.dart';
part 'note_dto.g.dart';

@freezed
class NoteDto with _$NoteDto {
  const factory NoteDto({
    required String title,
    required String description,
    // DTO可包含Entity没有的字段,比如服务器返回的ID
    @JsonKey(name: 'note_id') required String id,
  }) = _NoteDto;

  // 自动生成fromJson方法
  factory NoteDto.fromJson(Map<String, dynamic> json) => _$NoteDtoFromJson(json);
}

2. Repository中Entity与DTO的连接方式

需要转换方法,且可以通过Freezed简化实现:

  • 核心逻辑:Repository作为数据层与领域层的桥梁,负责将数据层的DTO转换为领域层的Entity;若需将Entity传给数据源(如保存到服务器),则要反向转换为DTO。
  • Freezed实现转换:可以在DTO类中添加自定义转换方法,或在Entity中添加从DTO构造的方法,这两种方式都能结合Freezed使用。

示例1:在DTO中添加toEntity方法

@freezed
class NoteDto with _$NoteDto {
  const factory NoteDto({
    required String title,
    required String description,
    @JsonKey(name: 'note_id') required String id,
  }) = _NoteDto;

  factory NoteDto.fromJson(Map<String, dynamic> json) => _$NoteDtoFromJson(json);

  // DTO转Entity的自定义方法
  NoteEntity toEntity() {
    return NoteEntity(
      title: title,
      description: description,
      id: id,
    );
  }
}

示例2:在Entity中添加从DTO构造的方法

@freezed
class NoteEntity with _$NoteEntity {
  const factory NoteEntity({
    required String title,
    required String description,
    String? id,
  }) = _NoteEntity;

  // 从DTO构造Entity
  factory NoteEntity.fromDto(NoteDto dto) {
    return NoteEntity(
      title: dto.title,
      description: dto.description,
      id: dto.id,
    );
  }
}

Repository中的使用示例

class NoteRepositoryImpl implements NoteRepository {
  final RemoteDataSource remoteDataSource;

  NoteRepositoryImpl(this.remoteDataSource);

  @override
  Future<NoteEntity> getNote(String id) async {
    // 从数据源获取DTO
    final noteDto = await remoteDataSource.fetchNote(id);
    // 转换为Entity返回给领域层
    return noteDto.toEntity();
    // 或使用Entity的fromDto方法:return NoteEntity.fromDto(noteDto);
  }

  @override
  Future<void> saveNote(NoteEntity note) async {
    // 将Entity转换为DTO传给数据源
    final noteDto = NoteDto(
      title: note.title,
      description: note.description,
      id: note.id ?? '',
    );
    await remoteDataSource.saveNote(noteDto);
  }
}

如果觉得手动写转换方法麻烦,也可以用Freezed的map/when方法(针对union类型),但上述方式最直观且符合Clean架构原则——转换逻辑仅存在于数据层与领域层的边界,不会污染其他层级。

内容的提问来源于stack exchange,提问作者BrML

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:02:45