Skip to main content

4.架构及分层规范

架构及分层图

如上所示:

myddd-electron架构整体分为以下层:

  • 视图模型层
  • 应用层
  • 领域层
  • 基础设施层(含网络,数据库,缓存等)

依赖注入

我们明确规定:使用依赖注入来注入各层的服务

iOC框架使用的是tsyringe

申明依赖注入

@injectable()
export class BlogNet {
}

如上所示,对于申明依赖注入非常简单,只需要在类前使用@injectable()就可以了。

如果希望以单例的模式注入,使用@singleton()来注入

注入依赖

方式一 : 使用@Inject注入

@injectable()
export class BlogNet {
private request: BaseRequest;

constructor(@inject(BaseRequest) baseRequest: BaseRequest) {
this.request = baseRequest;
}
}

使用constructor构建函数方式来申明流入

方式二: 使用getInstance获取实例

export interface IBlog {

private static blogNet: BlogNet;

private static getBlogNet(): BlogNet {
if (!this.blogNet) {
this.blogNet = InstanceFactory.getInstance(BlogNet);
}
return this.blogNet;
}

private static blogRepository: BlogRepository;

private static getBlogRepository(): BlogRepository {
if (!this.blogRepository) {
this.blogRepository = InstanceFactory.getInstance(BlogRepository);
}
return this.blogRepository;
}
}

第二种方式,使用InstanceFactory的方式来获取依赖。

视图层

此层对应React tsx文件。

参照示例

const BlogContent = observer(() => {
const [content, setContent] = useState("# loading");

const blogStore = useContext(BlogStore);

useEffect(() => {
fetchContent();
}, [blogStore.blog]);

const blogApplication: BlogApplication = InstanceFactory.getInstance(BlogApplication);

const fetchContent = async () => {
if (blogStore.blog) {
const content = await blogApplication.fetchBlogContent(blogStore.blog);
setContent((prev) => content);
}
};

return (
<Layout index={false}>
</Layout>
);
});

export default BlogContent;

视图层不能有业务上的逻辑,其业务逻辑需要全部封装到服务层中去

应用层

应用层为视图层提供服务。

应用层实现

import { Blog } from "modules/blogs/domain/Blog";
import { FileUtil } from "components/util/FileUtil";
import { FSDirUtil } from "components/util/FSDirUtil";
import { injectable } from "tsyringe";

@injectable()
export class BlogApplication {
public fetchLastestBlogs(): Promise<Blog[]> {
return Blog.fetchLastestBlogs();
}

public async fetchMoreBlogs(): Promise<Blog[]> {
return Blog.fetchMoreBlogs();
}

public async fetchBlogContent(blog: Blog): Promise<string> {
const blogFilePath = FSDirUtil.filePath(blog.slug);
const exists = await FileUtil.accessFile(blogFilePath);
if (!exists) {
const content = await blog.fetchContent();
await FileUtil.writeContentToFile(blogFilePath, content);
}
return (await FileUtil.contentOfFile(blogFilePath)).toString("utf-8");
}
}

服务层需调用领域层或缓存等基础设施层

领域层

将领域的一些业务逻辑封装在领域层中,提供给应用层调用。

export class Blog {
private static blogNet: BlogNet;

private static getBlogNet(): BlogNet {
if (!this.blogNet) {
this.blogNet = InstanceFactory.getInstance(BlogNet);
}
return this.blogNet;
}

private static blogRepository: BlogRepository;

private static getBlogRepository(): BlogRepository {
if (!this.blogRepository) {
this.blogRepository = InstanceFactory.getInstance(BlogRepository);
}
return this.blogRepository;
}

/**
* 获取更多博客
* @returns 返回博客列表
*/
public static async fetchMoreBlogs(): Promise<Blog[]> {
const last = await Blog.getBlogRepository().fetchMaxBlogDate();
const blogs = await Blog.getBlogNet().fetchMoreBlogs(last);
await Blog.getBlogRepository().batchSaveBlogs(blogs);
return blogs;
}

public static async fetchLastestBlogs(): Promise<Blog[]> {
return Blog.getBlogRepository().fetchLatestBlogs();
}

public fetchContent(): Promise<string> {
return Blog.getBlogNet().fetchBlogContent(this.slug);
}
}

如上,领域层需要依赖仓储接口及网络接口,将领域的行为封装在领域对象中。

基础设施层

仓储

提供对象数据存储及获取,查询功能。

myddd-electron数据库基于SQLITE3,项目基于SQLITE3封装了相关API。具体查看相关规范

仓储实现

@injectable()
export class BlogRepository {
private repository: Repository;

constructor(@inject(Repository) repository: Repository) {
this.repository = repository;
}

async fetchMaxBlogDate(): Promise<number> {
const sql = "select max(created) as _created from blog_";
const response = await this.repository.executeSingleQuery<IBlog>(sql);
if (response._created == null) return 0;
return response._created;
}

async fetchLatestBlogs(): Promise<Blog[]> {
const sql = "select * from blog_ order by created desc";
const results: IBlog[] = await this.repository.executeQuery<IBlog>(sql);

const blogs: Blog[] = [];
results.forEach((blog) => {
const tags: string[] = blog.tagData.split(",");
blog.tags = tags;
blogs.push(new Blog(blog));
});
return blogs;
}
}

网络

网络实现层

@injectable()
export class BlogNet {
private request: BaseRequest;

constructor(@inject(BaseRequest) baseRequest: BaseRequest) {
this.request = baseRequest;
}

public async fetchBlogContent(slug: string): Promise<string> {
const fetchUrl =
Config.getInstance().api +
"/api/collections/get/blogs?token=" +
Config.getInstance().accessToken;
const parmas = {
filter: {
slug: slug,
},
fields: {
content: 1,
},
};

const response = await this.request.requestForPost(fetchUrl, parmas);
if (response.resultSuccess()) {
const entries: any[] = response.data.entries;
return entries[0].content;
} else {
return "";
}
}
}

缓存

客户端程序,为提升速度,需在关键数据地方使用缓存,以提升体验。

myddd-electron封装了缓存的实现,简化开发

缓存接口

export interface ICache {
/**
* 存储对象到CACHE中
* @param key
* @param data
*/
setData(key:string,data:any):void;

/**
* 从CACHE中获取数据
* @param key
*/
getData(key:string):any;

/**
* 查询指定的KEY是否存在
* @param key
*/
exists(key:string):boolean;

/**
* 清除指定KEY的缓存
* @param key
*/
clearData(key:string):void;

/**
* 清除所有缓存
*/
clearAll():void;

}