话说小菜过做已近3年,虽出身PHP后项目大多涉及.net,系统也做得比较繁杂,从常见的CMS,企业OA,ERP,也涉及到电商系统的开发定制,爬虫,工具不一而足,其中web系统居多。

由于表现良好,时常会被经理叫去:“小菜,人是需要成长的啊,不如下个项目你负责吧,锻炼一下吧。”可是小菜对这种负责的职位是相当的不感冒,小菜只想快乐的编码,小菜的最终成为soho一族陪着美女环游世界。都知道事情一涉及到管理,负责就不那么纯洁了。并且小菜一直认为:将帅无能,累死三军。于是小菜每次都以“更对编码感兴趣” 为由拒绝。然而小菜知道丑媳妇早晚要见公婆,时间久了,这一步总是要过的,于是小菜也每每在项目之前,之后尝试以自己的理解,去搭一个架子,以备将来负责时不时之需。

于是“小菜白话搭架子”系列便出现了,会结合一些项目进行分析与记录,促进成长,水平有限,不喜勿喷哦。

闲话少绪,小菜给大家道一下整个的项目背景。

背景:此项目是为欧美某信息服务公司A加工其世界范围内所合作的的“信息提供商”提供的信息。也就是为A设计开发一个数据处理管道使用其不同的合作商,使不同合作商的数据最终为A所用。

由于小菜公司与A公司长久合作关系,大部分的合作商数据处理工具以开发完成,目前小菜们所要做的架子主要是将这些工具组合在一起,使开发过程减少人工干扰,减少人工成本。同时可适用于未来的需求扩展。

开发完成的Tool,及步骤:

  1. DownloadTool:从信息提供商下载数据。
  2. Diff:为数据提供商本身数据去重。
  3. Caculate:整合新版本数据与库中数据,生成新数据。
  4. View:生成用户可用数据。
  5. 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的更多相关文章

  1. [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. 软件模 ...

  2. matlab toolboxes 大全

    MATLAB Toolboxes top (Top) Audio - Astronomy - BiomedicalInformatics - Chemometrics  - Chaos - Chemi ...

  3. 软件开发学习笔记 <二>软件开发模型、Up、Rup、敏捷Up

    软件开发过程(process) 是一个将用户需求转化为软件系统所需要的活动的集合. 软件生命周期(SDLC,Software Devlopment Life Cycle) 软件从孕育.诞生.成长.成熟 ...

  4. DDD~概念中的DDD(转)

    概念中的DDD DDD: 领域驱动设计,它是对面向对象的的分析和设计(OOAD,Object Orient Analysis Design)的一个补充,对技术框架进行了分层规划,同时对每个类进行了策略 ...

  5. C++的学习资源

    本文总结了几个好的C++网站,以及C++方面的经典书籍.所列书籍或标准可以到这里找找电子版. wikipedia关于C++有关条目,注意看后面“参考文献”和“外部链接”: C++ programmin ...

  6. 读书笔记系列之java性能优化权威指南 一 第一章

    主题:java性能优化权威指南 pdf 版本:英文版 Java Performance Tuning 忽略:(0~24页)Performance+Acknowledge 1.Strategies, A ...

  7. DDD的思考

    概述 DDD领域驱动设计,它是对面向对象的的分析和设计(OOAD,Object Orient Analysis Design)的一个补充,对技术框架进行了分层规划,同时对每个类进行了策略和类型划分.领 ...

  8. UML Distilled - Development Process

    Iterative(迭代) and Waterfall(瀑布) Processes One of the biggest debates about process is that between w ...

  9. LiangNa Resum

    LiangNa AnShan Street, YangPu, NY @.com OBJECTIVE: Seeking a position to contribute my skills and ed ...

随机推荐

  1. 35,000FT大气压力的问题

    原来的问题:压力在喷气客机的飞行FL350(35,000脚)大约210mmHg.这是个大气压的数量? 在每平方英寸磅? 多少帕斯卡尔? http://wenwen.sogou.com/z/q33797 ...

  2. jquery datatables api (转)

    学习可参考:http://www.guoxk.com/node/jquery-datatables http://yuemeiqing2008-163-com.iteye.com/blog/20069 ...

  3. hdu1107武术(模拟题)

    Problem Description 在一个12行12方柱武侠世界,少林.武当峨眉武术弟子的三派,自相残杀主宰. 林世界的第一行的一列格子的坐标是(1, 1),第一行第二列坐标是(1, 2)--右下 ...

  4. shell 命名管道,进程间通信

    命名管道基础 命名管道也被称为FIFO文件, 在文件系统中是可见的,并且跟其它文件一样可以读写! 命名管道特点: 当写进程向管道中写数据的时候,如果没有进程读取这些数据,写进程会堵塞 当读取管道中的数 ...

  5. Xcode6为什么干掉pch(Precompile Prefix Header)&amp;怎样加入pch文件

    一直在用xcode6开发,但项目都是在xcode5上创建的,所以一直没注意到,xcode6居然干掉pch文件了. 为什么xcode6没有自己主动创建pch文件呢? 简单地看:我们在写项目的时候,大部分 ...

  6. Hadoop2.2.0--Hadoop Federation、Automatic HA、Yarn完全分布式集群结构

    Hadoop有很多的上场时间,与系统上线.手头的事情略少.So,抓紧时间去通过一遍Hadoop2在下面Hadoop联盟(Federation).Hadoop2可用性(HA)及Yarn的全然分布式配置. ...

  7. WinForm播放视频

    原文:WinForm播放视频 1背景 这几天一老友要求我做个小软件,在WinForm播放视频.印象中微软有个WM控件直接可以使用,晚上研究下 2实现方式 2.1微软草根 最简单的方式,是直接使用微软的 ...

  8. 使用gson和httpclient呼叫微信公众平台API

    吐槽:微信api很无语.有一部分xml.有一部分json. 最近看如何调用微信公众平台json有关api更方便.终于找到了httpcliect和gson对. 假设你有一个更好的办法,请告诉我. 了解如 ...

  9. [推荐]ORACLE PL/SQL编程详解之一:PL/SQL 程序设计简介(千里之行,始于足下)

    原文:[推荐]ORACLE PL/SQL编程详解之一:PL/SQL 程序设计简介(千里之行,始于足下) [推荐]ORACLE PL/SQL编程详解之一: PL/SQL 程序设计简介(千里之行,始于足下 ...

  10. ASP.Net TextBox控件只允许输入数字

    原文:ASP.Net TextBox控件只允许输入数字 1.1.在Asp.Net TextBox 控件的 OnKeyPress 事件中指定输入键盘码必须为数字: <asp:TextBox ID= ...