title: Flutter Clean Architecture - Conflict with domain entity and data model
body: <p>I am a beginner in flutter and have been learning flutter for 6 months or so. I am currently working on expense manager app. I found the clean architecture pattern really interesting although it requires a lot more(!) coding than needed otherwise. I could say this authentically since I almost made a functional app and now trying to start from scratch in clean architecture. I am not entirely following the guidelines in <a href="https://pub.dev/packages/flutter_clean_architecture" rel="nofollow noreferrer">https://pub.dev/packages/flutter_clean_architecture</a> but rather try to follow the clean concept as such -  <strong>Domain -&gt; Data -&gt; UI</strong></p>
  <p>Here is what I have done so far:</p>
  <p><strong>Domain</strong></p>
  <ol>
  <li>Created entities within Domain. Let me try to isolate the issue with one of the entities - user transaction</li>
  </ol>
  <pre><code>abstract class UserTransactionEntity {
    final int? transactionID;
    final String merchant;
    final amount;
    final String category;
    final String paymentType;
    final String paymentAccount;
    final String transactionDate;
    final String? transactionNotes;
    final String transactionType;
  
    ///User Transaction Entity Class
  
    UserTransactionEntity(
        {required this.transactionType,
        required this.merchant,
        this.amount,
        required this.category,
        required this.paymentType,
        required this.paymentAccount,
        required this.transactionDate,
        this.transactionNotes,
        this.transactionID});
  }
  
  </code></pre>
  <ol start="2">
  <li>Created a domain repository (interface?)</li>
  </ol>
  <pre><code>///UserTransaction Interface
  abstract class UserTransactionRepository {
    Future&lt;List&lt;UserTransactionEntity&gt;&gt; getTransactionList();
    postTransaction(UserTransactionEntity transaction);
  }
  
  </code></pre>
  <ol start="3">
  <li>Created separate usecases in Domain</li>
  </ol>
  <p>Base usecase</p>
  <pre><code>abstract class UseCase&lt;Type, Params&gt; {
    Future&lt;Type&gt; call(Params params);
  }
  
  class NoParams extends Equatable {
    @override
    List&lt;Object?&gt; get props =&gt; [];
  }
  
  </code></pre>
  <p>Usecase to fetch everything at once</p>
  <pre><code>class GetAllTransactions
      extends UseCase&lt;List&lt;UserTransactionEntity&gt;, NoParams&gt; {
    final UserTransactionRepository repository;
  
    GetAllTransactions(this.repository);
  
    @override
    Future&lt;List&lt;UserTransactionEntity&gt;&gt; call(NoParams params) async {
      print('calling GetAll Transactions UseCase');
      return await repository.getTransactionList();
    }
  }
  </code></pre>
  <p>Second use case to post transaction</p>
  <pre><code>
  class PostTransaction extends UseCase&lt;dynamic, UserTransactionEntity&gt; {
    final UserTransactionRepository _userTransactionRepository;
    PostTransaction(this._userTransactionRepository);
  
    @override
    call(UserTransactionEntity transaction) async {
      return await _userTransactionRepository.postTransaction(transaction);
    }
  }
  
  </code></pre>
  <p><strong>Data Layer</strong></p>
  <ol>
  <li>Created data models extending domain entities. I am using SQFLite so models are created with this in mind. I have kept the entities clean without any methods in it since it can vary depending on outer layers.</li>
  </ol>
  <pre><code>class UserTransactionModel extends UserTransactionEntity {
    UserTransactionModel(
        {int? transactionID,
        required String transactionType,
        required String merchant,
        required num amount,
        required String category,
        required String paymentType,
        required String paymentAccount,
        required String transactionDate,
        String? transactionNotes})
        : super(
              merchant: merchant,
              transactionType: transactionType,
              amount: amount,
              category: category,
              paymentType: paymentType,
              paymentAccount: paymentAccount,
              transactionDate: transactionDate,
              transactionNotes: transactionNotes,
              transactionID: transactionID);
  
    factory UserTransactionModel.fromMap(Map&lt;String, dynamic&gt; map) {
      return UserTransactionModel(
        merchant: map[kMerchant],
        transactionType: map[kTransactionType],
        amount: map[kTransactionAmount],
        category: map[kCategoryName],
        paymentType: map[kPaymentType],
        paymentAccount: map[kPaymentAccount],
        transactionDate: map[kTransactionDate],
        transactionNotes: map[kTransactionNotes],
        transactionID: map[kTransactionID],
      );
    }
  
    Map&lt;String, dynamic&gt; toMap() {
      _validation();
      return {
        kMerchant: merchant,
        kTransactionAmount: amount,
        kCategoryName: category,
        kPaymentType: paymentType,
        kPaymentAccount: paymentAccount,
        kTransactionDate: transactionDate,
        kTransactionNotes: transactionNotes,
        kTransactionType: transactionType
      };
    }
  
    @override
    String toString() {
      return &quot;User Transaction {$transactionType Amount: $amount Merchant:$merchant TransactionDate: $transactionDate}&quot;;
    }
  
    void _validation() {
      if (merchant == '') {
        throw NullMerchantNameException(message: 'Merchant should have a name!');
      }
    }
  }
  </code></pre>
  <ol start="2">
  <li>Created DB Provider for SQFlite and methods to post and fetch transactions</li>
  </ol>
  <pre><code>  getTransactionList() async {
      final _db = await _dbProvider.database;
      final result = await _db!.query(kTransactionTable);
      return result;
    } 
  
  </code></pre>
  <p>Post Transaction method is using data model created from domain entity.</p>
  <pre><code>  Future&lt;int&gt; postTransaction(UserTransactionModel userTransaction) async {
      var _resultRow;
      final _db = await _dbProvider.database;
  
      try {
        _resultRow = _db!.insert(
          kTransactionTable,
          userTransaction.toMap(),
        );
        return _resultRow;
      } catch (exception) {
        //TODO
        throw UnimplementedError();
      }
    }
  </code></pre>
  <ol start="4">
  <li>Created Local Data Source.</li>
  </ol>
  <pre><code>abstract class LocalDataSource {
    postTransaction(UserTransactionModel transaction);
    updateTransaction();
    deleteTransaction(int transactionID);
    getIncomeTransactionList();
    getExpenseTransactionList();
    getTransactionByDate(String date);
    Future&lt;List&lt;TransactionCategoryEntity&gt;&gt; getCategoryList();
    Future&lt;List&lt;UserTransactionEntity&gt;&gt; getTransactionList();
  }
  </code></pre>
  <p><strong>This is where my problem begins.</strong>
  Following method works fine in the 4th step when implemented in repository. Note that I am fetching entity from database.</p>
  <pre><code>    Future&lt;List&lt;UserTransactionEntity&gt;&gt; getTransactionList();
  </code></pre>
  <p>Following method throws error when try to implement in the data repository.</p>
  <pre><code>    postTransaction(UserTransactionModel transaction);
  </code></pre>
  <p>If I try to post this model, repository in 4th step complains that its conflicts with domain repository</p>
  <blockquote>
  <p>'UserTransactionRepositoryImpl.postTransaction' ('dynamic
  Function(UserTransactionModel)') isn't a valid override of
  'UserTransactionRepository.postTransaction' ('dynamic
  Function(UserTransactionEntity)').</p>
  </blockquote>
  <p>If I try to update it in the Local Data Source to domain entity, then it will ultimately conflict with the method in 2nd step to post data model. If I change everything to entity, it works fine but then the data model becomes useless and I have no way to created methods to remap data fetched from DB- e.g toMAP(), FromMap() etc.</p>
  <p>I am not sure what I am missing here. I am completely newbie in software programming but have some experience with VBA.</p>
  <p>Let's move on to next step.</p>
  <ol start="4">
  <li>Created a data repository implementing repositories from doamin layer.</li>
  </ol>
  <pre><code>class UserTransactionRepositoryImpl implements UserTransactionRepository {
    final LocalDataSource _dataSource;
    UserTransactionRepositoryImpl(this._dataSource);
  
    @override
    Future&lt;List&lt;UserTransactionEntity&gt;&gt; getTransactionList() async {
      try {
        final List&lt;UserTransactionEntity&gt; transactionList =
            await _dataSource.getTransactionList();
        return transactionList;
      } on Exception {
        throw Exception;
      }
    }
  
    @override
    postTransaction(UserTransactionEntity transaction) async {
      await _dataSource.postTransaction(transaction);
    }
  }
  
  </code></pre>
  <p><strong>Presentation layer</strong>
  Finally, I have created the Presentation layer as well and isolated the UI from data layer with the help of Provider. I have set aside the UI part to the final stage unlike my previous experiments.</p>
  <p><strong>Questions:</strong></p>
  <p>So to conclude such a long writeup, here my questions are;</p>
  <ol>
  <li><p>When we create data models extending entity, what should be the one called in UI - data model or domain entity? I can make it work with both but then data model becomes useless especially when we post data using entity and retrieve same thing. Also, this requires the entities to have proper methods to map data for SQLite. If I change to Firebase, the domain logic to format data needs change, which breaks the core principle - closed for edit and most importantly data layer affects domain layer.</p>
  </li>
  <li><p>Is data model used only when the data is fetched externally and needed extra work to make it look like an entity data? Or in other words, if I am just posting a transaction and read in within the app, would entity suffice?</p>
  </li>
  <li><p>If both should be used, how it should be done?</p>
  </li>
  <li><p>Finally, help me understand what went wrong.</p>
  </li>
  </ol>