ToolBox Analysis & Design
话说小菜过做已近3年,虽出身PHP后项目大多涉及.net,系统也做得比较繁杂,从常见的CMS,企业OA,ERP,也涉及到电商系统的开发定制,爬虫,工具不一而足,其中web系统居多。
由于表现良好,时常会被经理叫去:“小菜,人是需要成长的啊,不如下个项目你负责吧,锻炼一下吧。”可是小菜对这种负责的职位是相当的不感冒,小菜只想快乐的编码,小菜的最终成为soho一族陪着美女环游世界。都知道事情一涉及到管理,负责就不那么纯洁了。并且小菜一直认为:将帅无能,累死三军。于是小菜每次都以“更对编码感兴趣” 为由拒绝。然而小菜知道丑媳妇早晚要见公婆,时间久了,这一步总是要过的,于是小菜也每每在项目之前,之后尝试以自己的理解,去搭一个架子,以备将来负责时不时之需。
于是“小菜白话搭架子”系列便出现了,会结合一些项目进行分析与记录,促进成长,水平有限,不喜勿喷哦。
闲话少绪,小菜给大家道一下整个的项目背景。
背景:此项目是为欧美某信息服务公司A加工其世界范围内所合作的的“信息提供商”提供的信息。也就是为A设计开发一个数据处理管道使用其不同的合作商,使不同合作商的数据最终为A所用。
由于小菜公司与A公司长久合作关系,大部分的合作商数据处理工具以开发完成,目前小菜们所要做的架子主要是将这些工具组合在一起,使开发过程减少人工干扰,减少人工成本。同时可适用于未来的需求扩展。
开发完成的Tool,及步骤:
- DownloadTool:从信息提供商下载数据。
- Diff:为数据提供商本身数据去重。
- Caculate:整合新版本数据与库中数据,生成新数据。
- View:生成用户可用数据。
- Backup:数据备份。
因为小菜认为做开发解决方案一定要在技术选型之前,如果先做技术选型的话,可能会我们的思考产生束缚,
小菜第一次获取以上信息时,由于这么多的Tool,将来还可能扩展,本能的想到了一个模型,就是ToolBox。
这样的话,将来那些新开发的Tool或已有的Tool之间就不需要产生本质的结合,将来我们就需要将他们扔进一个叫做“Toolbox"的文件夹中,MainServer会检索这个文件夹,将新增的Tool加载进系统运行程序集,有些类似于插件开发。由于经理对于Wcf有个人的青睐,要求使用wcf做选型的分布式信息架构,说实话小菜不喜欢wcf,因为小菜一直认为这又是微软的一个学院派产品,配置麻烦也不酷。不过用就用吧。
于是小菜认为,既然头脑中有了模型,技术有了选型,开始最喜欢的代码吧:
小菜的结构与分层大概是这样的:
小菜为每一个Tool都做了一个接口,MainServer和Tool之间通过wcf进行通信,mainserver通过检测Tool的心跳获知其生活状态,Tool内有一个线程定时回调方法去Mainserver查找有没有分配自己的Task,Tool获取Task之后执行,返回给客户端:
代码如下:
server.contract:
[ServiceContract] public interface IView { [OperationContract] Int32 CreateWork(UpstreamViewsWF work, Int32? seriesID); [OperationContract] Boolean TryTakeWork(out GeneralWork<UpstreamViewsWF> work); [OperationContract] void UpdateWork(GeneralWork<UpstreamViewsWF> work); [OperationContract] void DeleteWork(Int32 id); [OperationContract] void ReportProcess(WorkProgress progress); [OperationContract] void ReportCompletion(GeneralWork<UpstreamViewsWF> work); }
service:
public class View: ApplicationService, IView {
private Logger _logger;
private IGeneralJobRepository _generalRepo;
private IJobStatusHistoryRepository _historyRepo; public ViewsService(
IRepositoryContext context,
IGeneralJobRepository generalJobRepo,
IJobStatusHistoryRepository historyRepo)
: base(context) {
_generalRepo = generalJobRepo;
_historyRepo = historyRepo;
_logger = LogManager.GetCurrentClassLogger();
} public Int32 CreateWork(Views work, Int32? seriesID) {
_logger.Info(String.Empty);
GeneralJob job = new GeneralJob {
Category = (Byte)WorkCategory.Views,
Status = (Byte)WorkStatus.Created,
}; if (seriesID.HasValue && seriesID.Value > 0) {
job.SeriesID = seriesID.Value;
} work.TypeName = WorkCategory.Views.ToString(); job.VARS = JsonConvert.SerializeObject(work);
try {
Context.BeginTrans();
_generalRepo.Create(job);
_historyRepo.Create(CreateJobStatus(job)); Context.Commit();
return job.ID;
}
catch (Exception ex) {
_logger.ErrorException(String.Empty, ex);
throw FaultData.CreateFaultException(ex);
}
} public GeneralWork<Views> RetriveWork(int id) {
_logger.Info(String.Empty);
try {
GeneralJob job = _generalRepo.RetriveByKey(id);
return job.Map<Views>();
} catch (Exception ex) { _logger.ErrorException(String.Empty, ex); throw FaultData.CreateFaultException(ex); } } public void UpdateWork(GeneralWork<Views> work) { _logger.Info(String.Empty); try { GeneralJob job = _generalRepo.RetriveByKey(work.ID); if (job.Status != (Byte)WorkStatus.Created) { Exception ex = new Exception("Only new work could be modified"); } work.VARS.TypeName = WorkCategory.UpstreamViewsWF.ToString(); job.SeriesID = work.SeriesID; job.VARS = JsonConvert.SerializeObject(work.VARS); Context.BeginTrans(); _generalRepo.Update(job); _historyRepo.Create(CreateJobStatus(job)); Context.Commit(); } catch (Exception ex) { _logger.ErrorException(String.Empty, ex); throw FaultData.CreateFaultException(ex); } } public void DeleteWork(Int32 id) { _logger.Info(String.Empty); try { GeneralJob job = _generalRepo.RetriveByKey(id); if (job.Status != (Byte)WorkStatus.Created) { Exception ex = new Exception("Only new work could be modified"); } job.Status = (Byte)WorkStatus.Abandoned; Context.BeginTrans(); _generalRepo.Delete(job); _historyRepo.Create(CreateJobStatus(job)); Context.Commit(); } catch (Exception ex) { _logger.ErrorException(String.Empty, ex); throw FaultData.CreateFaultException(ex); } } public void ReportProcess(WorkProgress progress) { _logger.Info(String.Empty); } public void ReportCompletion(GeneralWork<Views> work) { _logger.Info(String.Empty); try { GeneralJob job1 = work.Map<Views>(); job1.Status = (Byte)WorkStatus.Finished; Context.BeginTrans(); _generalRepo.Update(job1); _historyRepo.Create(CreateJobStatus(job1)); Context.Commit(); } catch (Exception ex) { _logger.ErrorException(String.Empty, ex); throw FaultData.CreateFaultException(ex); } } public GeneralWork<Views>[] ReadWorkQueue(int currentPage, int itemsPerPage, WorkStatus? status, string key, outPageClause pageClause) { _logger.Info(String.Empty); try { List<Expression<Func<GeneralJob, Boolean>>> filters = new List<Expression<Func<GeneralJob, bool>>>(); filters.Add(e => e.Category == (Byte)WorkCategory.UpstreamViewsWF); if (status.HasValue) { filters.Add(e => e.Status == (Byte)status); } if (!String.IsNullOrWhiteSpace(key)) { filters.Add(e => e.VARS.Contains(key.Trim())); } PageClause<GeneralJob> jobs = _generalRepo.RetriveAll(currentPage, itemsPerPage, true , p => p.ID, filters.ToArray()); pageClause = new PageClause(jobs); return jobs.Map<UpstreamViewsWF>().ToArray(); } catch (Exception ex) { _logger.ErrorException(String.Empty, ex); throw FaultData.CreateFaultException(ex); } } }
对Tool的封装:
class Program { static lWork<View> currentWork; private readonly static string exePath = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "app.exe" ); static void Main( string[] args ) { Trace.Listeners.Add( new ConsoleTraceListener() ); try { if( TryTakeWork() ) { Trace.WriteLine( "Take a work" ); Work(); ProcessWork(); ReportCompletion(); } } catch( Exception ex ) { Console.WriteLine( ex ); } } static void Work() { using( ServiceFactory factory = new ServiceFactory() ) { try { ISeriesWorkService service = factory.CreateChanel<ISeriesWorkService>(); SeriesWork seriesWork = service.RetriveWork( currentWork.SeriesID ); currentWork.VARS.TypeName = ConfigurationManager.AppSettings[ "TypeName" ]; Trace.WriteLine( "TypeName:" + currentWork.VARS.TypeName ); currentWork.VARS.Code = seriesWork.egion; Trace.WriteLine( "Code:" + Code ); CopyHelper.CopyDir( Path.GetFullPath( @"..\..\Application\" ), AppDomain.CurrentDomain.BaseDirectory ); } catch( Exception ex ) { Console.WriteLine( ex ); } } } static Boolean TakeWork() { Trace.WriteLine( "TryTakeWork" ); using( ServiceFactory factory = new ServiceFactory() ) { IUpstreamViewsWFService service = factory.CreateChanel<IUpstreamViewsWFService>(); return service.TakeWork( out currentWork ); } } static void ProcessWork() { Trace.WriteLine( "ProcessWork..." ); Process process = null; try { ProcessStartInfo info = new ProcessStartInfo(); info.CreateNoWindow = false; info.FileName = exePath; //as u need process = Process.Start( info ); process.EnableRaisingEvents = true; process.WaitForExit(); Trace.WriteLine( "ProcessWork succeed" ); } catch( Exception EX ) { Trace.WriteLine( "Exception:" + EX ); if( process != null ) { process.Kill(); } } } static void ReportCompletion() { Trace.WriteLine( "ReportCompletion" ); using( ServiceFactory factory = new ServiceFactory() ) { IView service = factory.CreateChanel<IView>(); service.ReportCompletion( currentWork ); } } }
话说一天过去了,小菜把基本的功能完成了,小菜想反正也有时间于是小菜把他知道的东西都用上了IOC(unit),Log4,Orm(ef)。慢慢的小菜开始将接口丰富了越来越多的功能,最后小菜对经理说了自己的想法,说了用的技术,经理说好"下午部署上吧“,小菜心想我的接口已经有了很丰富的功能,我还提供了很多其他功能的接口,肯定没问题于是小菜欣然的答应了。然而新的挑战就这样悄然的反生了。
他找到每个Tool的开发人员讲解自己的接口怎样用,能做什么,需要与MainServer以怎样的方式进行通信,不同异常机制信息的提供的不同。小菜发现他对每一开发者讲解的时候,大家更多的是关心自己的Tool应该更少的关注Mainserver,同时大家也更多的是忙于自己的事情,没有多少时间去协同维护你的接口,大家的一致决定是你先写一个Tool对接Mainserver的Demo我们照着改就可以了,你先写吧。
就这样小菜心想,我已经写的这么好了的接口,为什么大家不喜欢用不喜欢听呢。下班之后小菜一直不思其解,于是拨通了同校大两级也是做开发的学长”老鸟“的电话,简单介绍了事情的经过之后,老鸟呵呵一笑说:“小菜, 你的想法不错,可是就是因为你提供的东西反而太多了,大师不是只懂加法也要会做减法,搭架子要有所为有所不为”。
未完待续。。。
ToolBox Analysis & Design的更多相关文章
- [Design Patterns] 3. Software Pattern Overview
When you're on the way which is unknown and dangerous, just follow your mind and steer the boat. 软件模 ...
- matlab toolboxes 大全
MATLAB Toolboxes top (Top) Audio - Astronomy - BiomedicalInformatics - Chemometrics - Chaos - Chemi ...
- 软件开发学习笔记 <二>软件开发模型、Up、Rup、敏捷Up
软件开发过程(process) 是一个将用户需求转化为软件系统所需要的活动的集合. 软件生命周期(SDLC,Software Devlopment Life Cycle) 软件从孕育.诞生.成长.成熟 ...
- DDD~概念中的DDD(转)
概念中的DDD DDD: 领域驱动设计,它是对面向对象的的分析和设计(OOAD,Object Orient Analysis Design)的一个补充,对技术框架进行了分层规划,同时对每个类进行了策略 ...
- C++的学习资源
本文总结了几个好的C++网站,以及C++方面的经典书籍.所列书籍或标准可以到这里找找电子版. wikipedia关于C++有关条目,注意看后面“参考文献”和“外部链接”: C++ programmin ...
- 读书笔记系列之java性能优化权威指南 一 第一章
主题:java性能优化权威指南 pdf 版本:英文版 Java Performance Tuning 忽略:(0~24页)Performance+Acknowledge 1.Strategies, A ...
- DDD的思考
概述 DDD领域驱动设计,它是对面向对象的的分析和设计(OOAD,Object Orient Analysis Design)的一个补充,对技术框架进行了分层规划,同时对每个类进行了策略和类型划分.领 ...
- UML Distilled - Development Process
Iterative(迭代) and Waterfall(瀑布) Processes One of the biggest debates about process is that between w ...
- LiangNa Resum
LiangNa AnShan Street, YangPu, NY @.com OBJECTIVE: Seeking a position to contribute my skills and ed ...
随机推荐
- 运营商网络採用SDN所面临的挑战(一)
运营商网络採用SDN所面临的挑战(一) Babak Samimi 将数据平面.控制平面与管理平面分隔开来所实现的软件定义网络(SDN)改善了OPEX及CAPEX,而且使得网络资源的集中调配和管理成为可 ...
- Java迭代器[转]
迭代器是一种模式,它可以使得对于序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的.只要拿到这个对象,使用迭代器就可以遍历这个对象的内部. 1.Iterator ...
- React.js再探(四)
不知道看官们还记不记得上一节的内容,关于生命周期的.我们来个例子重温且练习一下. 传送门:http://www.cnblogs.com/galenyip/p/4574400.html 我们来实现一下时 ...
- 出现localStorage错误Link解决方案(组态)
属性-链接-进入-附加依赖-加入sqlite3.lib cocos2d-x-2.2.2\Debug.win32添加的文件夹sqlite3.dll.sqlite3.lib 版权声明:本文博客原创文章.博 ...
- 教你一步一步部署.net免费空间OpenShift系列之二------创建应用
接上回书,注册完毕后需要在Openshift上创建一个应用空间,如如何创建空间呢,Openshift本身是不直接支持Mono来部署ASP.Net程序的,需要借助openshift-community- ...
- Swift语言指南(四)--类型安全和类型推断
原文:Swift语言指南(四)--类型安全和类型推断 Swift是一门类型安全语言,类型安全语言需要代码里值的类型非常明确.如果你的代码中有部分值需要String类型,你就不能错误地传递Int. 鉴于 ...
- cocos2d-x 3.1.1 学习笔记[13] listen 监听器
文章出自于 http://blog.csdn.net/zhouyunxuan //创建监听器 auto listen = EventListenerTouchOneByOne::create(); ...
- angular.js的路由和模板在asp.net mvc 中的使用
angular.js的路由和模板在asp.net mvc 中的使用 我们知道angular.js是基于mvc 的一款优秀js框架,它也有一套自己的路由机制,和asp.net mvc 路由不太一样.as ...
- AngularJS + Node.js + MongoDB开发
AngularJS + Node.js + MongoDB开发的基于位置的通讯录(by vczero) 一.闲扯 有一天班长说了,同学们希望我开发一个可以共享位置的通讯录,于是自己简单设计了下功能.包 ...
- AutoMapper 创建嵌套对象映射(原创)
之前在做DTO转换时,用到AutoMapper.但DTO的层次太深了,无奈官方没针对嵌套类型提供好的解决方案,于是自己实现了一下: 思路:采用递归和反射很好的避免手工创建嵌套对象的映射. 第一个版本, ...