执行多项操作并提交

use*_*er7 5 java api-design

系统处理两种类型的资源。有用于管理资源的写入和删除API。客户端(用户)将使用库API来管理这些资源。每次写入(或创建)资源都会导致更新存储或数据库。

该API如下所示:

1)创建库客户端。用户将使用返回的客户端对资源进行操作。

MyClient createClient(); //to create the client
Run Code Online (Sandbox Code Playgroud)

2)MyClient界面。提供对资源的操作

writeResourceType1(id);
deleteResourceType1(id);

writeResourceType2(id);
deleteResourceType2(id);
Run Code Online (Sandbox Code Playgroud)

一些资源依赖于另一个。用户可能会无序地写入它们(可能在写入依赖项之前先写入资源)。为了防止系统处于不一致状态,所有更改(资源更新)都将写入到暂存位置。当用户指示他/她已写完所有内容时,所做的更改才会写入实际存储中。

这意味着我需要在上述接口中使用commit方法MyClient。因此,访问模式将如下所示

Client client = provider.createClient();
..
client.writeResourceType1(..)

client.writeResourceType1(..)

client.deleteResourceType2(..)

client.commit(); //<----
Run Code Online (Sandbox Code Playgroud)

我不习惯在MyClient界面中使用提交API 。我觉得它在污染它,错误的是错误的抽象级别。

有没有更好的方法来解决这个问题?


我想到的另一个选择是将所有更新作为单个调用的一部分。该API将充当批处理API

writeOrDelete(List<Operations> writeAndDeleteOpsForAllResources)
Run Code Online (Sandbox Code Playgroud)

不利的一面是,用户必须将其端部的所有操作组合起来才能调用它。单个调用中也塞满了太多内容。因此,我不倾向于这种方法。

Mar*_*nik 10

虽然您介绍的两种方法都是可行的选择,但事实是,在某个时间点,用户必须以某种方式说:“好,这些是我的更改,全部接受或保留。” 这正是IMO的提交。并且仅此一项就必须进行某种必须在API中进行的调用。

在第一种方法中,您已经展示了其明显的显式方法,并已通过commitmethod 完成。在第二种方法中,它相当隐式,由传递给writeOrDelete方法的列表的内容确定。

因此,以我的理解,提交必须以某种方式存在,但是问题是如何使它减少“烦人”的问题:)

这里有一些技巧:

技巧1:Builder / DSL

interface MyBuilder {
   MyBuilder addResourceType1(id);
   MyBuilder addResourceType2(id);
   MyBuilder deleteResourceType1/2...();
   BatchRequest build();
}

interface MyClient {
   BatchExecutionResult executeBatchRequest(BatchRequest req);
}
Run Code Online (Sandbox Code Playgroud)

此方法或多或少类似于第二种方法,但是它具有“添加资源”的明确方法。一个单一的创建点(几乎不喜欢MyClient,只是我相信最终它将有更多的方法,所以也许分离是一个好主意。正如您所说:“我不愿意在MyClient接口中使用commit API。我感觉它正在污染它,并且错了,它是错误的抽象级别。“)这种方法的另外一个论点是,现在您知道在使用此方法的代码中存在一个生成器及其“抽象概念”,您不会不必考虑传递对列表的引用,也不必考虑如果有人调用clear()此列表中的内容会发生什么,依此类推。构建器具有精确定义的API,可以完成哪些工作。

在创建构建器方面:

您可以使用诸如Static Utility类之类的东西,甚至可以向以下方法添加方法MyClient

// option1

public class MyClientDSL {
   private MyClientDSL {}
   public static MyBuilder createBuilder();
}

// option 2
public interface MyClient {
    MyBuilder newBuilder();
}
Run Code Online (Sandbox Code Playgroud)

对此方法的引用:JOOQ(它们具有这样的DSL),OkHttp,它们具有Http请求的构建器,实体等(与OkHttpClient本身分离)。

技巧2:提供执行代码块

现在这可能很难实现,具体取决于您在哪种环境下运行,但是基本上是从Spring借用的一个想法:为了保证在处理数据库时进行事务处理,它们提供了一个特殊的注释@Transactional,该注释通常放在方法上:“方法中的所有内容都在事务中运行,我将自己提交它,以便用户完全不处理事务/提交。我还将回退异常”

所以在代码中看起来像:

class MyBusinessService {
       private MyClient myClient; // injected
         @Transactional
         public void doSomething() {
             myClient.addResourceType1();
             ...
             myClient.addResourceType2();
             ...  
         }
}
Run Code Online (Sandbox Code Playgroud)

在后台,他们应该维护ThreadLocals才能在多线程环境中实现这一点,但重点是该API很干净。该方法commit可能存在,但在大多数情况下可能不会使用,更不用说真正复杂的场景了,用户可能真的“需要”这种细粒度的控件。

如果您使用spring /其他管理代码的容器,则可以将其与spring集成(实现此目的的技术方法不在此问题的范围内,但您可以理解)。

如果没有,您可以提供最简单的方法:

public class MyClientCommitableBlock {

    public static <T> T executeInTransaction(CodeBlock<T> someBlock)
            builder) {

          MyBuilder builder = create...;
          T result = codeBlock(builder);
          // build the request, execute and commit
          return result;
      }
}
Run Code Online (Sandbox Code Playgroud)

外观如下:

static import MyClientCommitableBlock.*;
public static void main() {

     Integer result = executeInTransaction(builder -> {
        builder.addResourceType1();
        ... 
        return 42;
     });
}

// or using method reference:

    class Bar {
       Integer foo() {
         return executeInTransaction(this::bar);    
       } 

       private Integer bar(MyBuilder builder) {
         ....
       }
    }
Run Code Online (Sandbox Code Playgroud)

在这种方法中,构建器在仍精确定义一组API的同时可能不会向最终用户公开“显式”提交方法。相反,它可以在MyClientCommitableBlock类中使用一些“ package private”方法


Cle*_*ath 0

试试这是否适合你

让我们在暂存表中有一个标志,其中列名为status

状态列值

New : Record inserted by user
ReadyForProcessing : Records ready for processing
Completed : Records processed and updated in Actual Store
Run Code Online (Sandbox Code Playgroud)

添加以下方法而不是commit(),一旦用户调用此方法/服务,就拾取该用户的记录并将status: New其从暂存位置发布到实际存储中

client.userUpdateCompleted();
Run Code Online (Sandbox Code Playgroud)

还有另一种选择,让我们通过给予来消除客户端干预,client.commit(); or client.userUpdateCompleted();取而代之的是,我们可以batch process using Scheduler以特定的时间间隔运行,扫描暂存表并将有意义的和用户更新已完成的记录填充到实际存储中