程序员 2014-11-4 每日奇妙之旅
发布于 4个月前 作者 leogiese 515 次浏览 来自 分享

原文 http://forjs.org/book/xyfULqrrXg/section/gkeWOATQXPg

程序员 2014-11-4 每日奇妙之旅

推荐:《精通Node.js开发》 《Angular.js视频详解》 腾讯QQ 1405491181 微信号 forjs_org

先做个计划

domain框架开发

  • Event 已完成

  • Actor 完善 1个番茄钟

  • Actor 单元测试 1个番茄钟

  • Repository 完善 3 个番茄钟

  • Repository 单元测试 1个番茄钟

  • EventBus 完善 2个番茄钟

  • EventBus 单元测试 1个番茄钟

  • Domain 完善 2个番茄钟

  • Domain 单元测试 1个番茄钟

今天的重要且紧急的任务,就是完成domain框架开发,并进行必要的测试。

沏一杯咖啡,开始奇妙之旅吧!这是一个激动人心的创造过程。

完善 Actor

需要先修改一下Actor#listen方法,我准备把所有方法都通过provider方式提供。

[listen](eventName, handleName) {
    this.emit('listen', {eventName: eventName, handleName: handleName});
}

然后修改 call 方法

call(commandName, data, caller) {
    this[commandName](data,{
        apply:(eventName, data)=> {
            this[apply](eventName, data, caller);
        },
        listen:this[apply].bind(this)
    });
}

e.g.

it("#listen", function (done) {
    var User = Actor.extend({
        typeName:"User",
        methods:{
            changeName: function (name,service) {
                service.listen("change","finishChange");
            },
            finishChange: function (data, service) {
                done();
            }
        }
    })

    var user = new User();

    user.on("listen", function (event,methodname) {
        this.call("finishChange",methodname);
    });

    user.call("changeName");
})

这个单元测试代码,我对 apply测试修改并测试通过 ,但 #listen 这个测试没通过,还需要找一下原因。

另外我对listen方法的实现方式不满意,还需要进一步优化。

原因找到了,是call方法的bug,正确代码是:

call(commandName, data, caller) {
    this[commandName](data,{
        apply:(eventName, data)=> {
            this[apply](eventName, data, caller);
        },
        listen:this[listen].bind(this) // here
    });
}

Actor开发基本这样,当然这只是preview,我觉得不够完善的,还是构造器部分错误处理和异常处理,之后逐步完善吧。

还有就是 listen 方法,我觉得应该优化一下,不过眼下够用了。

我把 when 修改成私有,通过注入方式加入到各个方法里,另外把call进行了修改:

[when](event, set) {
    if (event.name === "remove") {
        set("alive", false);
    }

    if(this[otherWhen])
    this[otherWhen](event, set);
}

extend方法进行了修改,把when注入到各个方法里

static extend(options) {

    var typeName = options.typeName;
    if (typeNames.indexOf(typeName) !== -1)
        throw new Error("type name is exist.")

    var methods = options.methods;

    class Type extends Actor {
    }

    Object.defineProperty(Type.prototype, "typeName", {
        get() {
            return typeName;
        }
    })

    // 这是新加的
    Object.defineProperty(Type.prototype, otherWhen, {
        value: options.when
    })

    for (var k in methods) {
        Object.defineProperty(Type.prototype, k, {
            value: methods[k]
        })
    }

    return Type;
}

用一个单元测试when

it("#when", function () {
    var User = Actor.extend({
        typeName:"People",
        methods:{
            changeName: function (name,service) {
                service.apply("changeName",name);
            }
        },
        when: function (event, set) {
            if(event.name === "changeName"){
                set("name",event.data.data);
            }
        }
    })

    var user = new User();

    user.call("changeName","leo");
    user.get("name").should.eql("leo");
})

Repository 的完善

一个仓储需要那些方法呢?

  • 思考如何构造一个仓储对象
  • create 创建一个Actor
  • get 得到一个Actor对象
  • remove 删除一个Actor

一个仓储需要 Actor类和 eventstore 事件储存对象。

class Repository {

    constructor(Actor, eventstore) {
        this.Actor = Actor;
        this.eventstore = eventstore;
        this[cache] = {}
    }
}

然后是create方法

*create(data) {
    let actor = new this.Actor(data);
    let result = yield this.getFromSnapShot(actor.id);
    let err = result[0];
    let snapshot = result[1];
    let stream = result[2];
    err = (yield this.createSnapshot({
        aggregateid: actor.id,
        aggregate: this.Actor.name,
        data: actor.json,
        revision: stream.revision
    }))[0];

    if (err) {
        throw err;
    } else {
        this[cache][actor.id] = actor;
        return actor;
    }
}

进行单元测试创建一个Repository对象

var Repository = require("../dist/lib/Repository");
var Actor = require("../dist/lib/Actor");
var ES = require("eventstore");

describe("Repository", function () {

    var User = Actor.extend({
        typeName:"User"
    }),es = new ES,repos;

    it("#new", function () {
       repos  = new Repository(User,es);
    })
})

测试通过,很lucky,上午就这样,下午继续奇妙之旅吧!

继续 …

*create(data) {
    let actor = new this.Actor(data);
    let result = yield this.getFromSnapShot(actor.id);
    let snapshot = result[0];
    let stream = result[1];
    yield this.createSnapshot({
        aggregateId: actor.id,
        aggregate: this.Actor.name,
        data: actor.json,
        revision: stream.revision
    })

    this[cache][actor.id] = actor;
    return actor;
}

测试代码:

it("#create", function (done) {
    co(function *() {
        var user = yield repos.create({name:"leo"});
        user.get("name").should.eql("leo");
        var getFromSnapShot = thunkify(es.getFromSnapshot).bind(es);
        var rs = yield getFromSnapShot(user.id);
        var snap = rs[0];
        should.exist(snap);
        done();
    })()
})

下面实现 get 方法

*get(id) {

    let actor;

    if (actor = this.getFromCache(id)) {
        return actor;
    }

    let result = yield this.getFromSnapShot(id);

    if (actor = this.getFromCache(id)) {
        return actor;
    }

    let snapshot = result[0];
    let stream = result[1];

    if(!snapshot){
        return null;
    }

    let snap = snapshot.data;
    let history = stream.events;
    actor = new this.Actor();

    // mix aggregate object.
    actor.loadSnap(snap);

    var historyv = [];

    history.forEach(function (e) {
        historyv.push(e.payload);
    })

    actor.loadEvents(historyv);

    this[cache][actor.id] = actor;

    return actor;

}

单元测试

it("#get", function (done) {
    co(function* () {
        repos.clear(uid);
        var user = yield repos.get(uid);
        user.get("name").should.eql("leo");
        done();
    })()
})

ok,测试通过!

1 回复

不要恶意顶贴

回到顶部