Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Dynamic Process Map for win32 Application using Delphi

Delphi XE , Win32 Application , SQL Server 2005 Database.

I am implementing a Quality Management System. I have some predefined Process Maps to apply them inside my application/system. I am requested to have all the transactions (I am not sure if it is correct word for it) dynamic so whenever they modify the process maps it affects the application (Without recompilation or any patches of course)

Here is an example to explain more clear:

Assume a Document controlling module, We have a process map as :

  1. [Document Controller] Receives the document from Contractor
  2. [Document Controller] Checks the document with the checklist
  3. [Document Controller] Sends the document to the [Project Manager]
  4. [Project Manager] applies and Action in the document
  5. [project Manager] Sends the document to [Document Controller]
  6. [Document Controller] Archives the document.

Now application should read the parameters from a database for its functions. Let’s say Received and Checked the document (1 and 2) and now sends it. As soon as the “Save” Button is pressed the system should check who should be the receiver of this document and send the document to him/her. In our example, the receiver is the [project Manager]. however, sometime later they might decide to change the process map as - “3- [Document Controller] sends the document to the [Project Architect]”. Therefore, the system should act as defined in the process map.

I am wondering what Is the proper way to implement such system (Delphi XE , win32)?

I have some idea but not sure if it is proper : For each Process in the process map I can define a Service with a kind of unique Id and I read the service from the database and call it in the application layer (with relevant Parameters). In this case I am not sure if each service should be a dll or package file and I believe it is wrong to have that number of library files, since the services are going to not very few!

I hope I could explain my problem well and sorry if it is too long. Please let me know if it is not clear.

Thanks,
Mahya

like image 225
Mahya Avatar asked Nov 05 '22 20:11

Mahya


1 Answers

It sounds to me like you want to apply some common rules for every "business function" performed by your application, thereby implementing some form of quality management. I'm going to use "business function" to denote a logical operation that may span several technical functions and procedures in the source code.

There is no defined "proper way", but some ideas are better than others.

The use of a database to store dynamic data is obviously a good idea. For example you can model each business function as a separate entity (with one database record per business function). Whatever variables you'll need to manage the processing of each business function will determine the necessary fields. You'll definitely need a unique id for each one.

There's no reason to code the business functions into separate dlls, but I'd put them in separate units in the source code or maybe group the functions according to their type of operation or some other logical grouping that makes sense to your business. You'll need to change the way you call your business functions. You'll need to call them indirectly, in other words you would call a generic PerformFunction routine, perhaps passing a function identifier and some other parameters in whatever data structure suits you. As the parameters are changed in the database (according to your example, who to send the Document to, Project Manager or Project Architect?) the operation of the business function will be modified accordingly as long as you've implemented all the possible permutations that could arise given the number of variables for each business function. I'm sure the Document Recipient's email address isn't all you have in mind. Here's some code that might help. I'm not saying it's good code, it's only to convey the idea.

type
  TFunctionRuntimeParameter = record
    FunctionID: integer; // this better be unique
    FunctionName: string;  // something intelligible for display purposes
    ReportToEmail: string; // who to send the end report document
    AuditLevel: integer;  // for example vary the degree of auditing/logging
    variableABC: TDateTime;  // could be a date that influences something
  end;

function TMyForm.GetRuntimeParametersFromDB(aFunctionID: integer): TFunctionRuntimeParameters;
var
  tempResult: TFunctionRuntimeParameters;
begin
  // For brevity I'm going to assume an existing query object connected to your db.
  qry.SQL.Add('Select * From BusinessFunctions Where Function_ID = :FunctionID');
  qry.ParamByName('FunctionID').AsInteger := aFunctionID;
  qry.Open;
  tempResult.FunctionID := qry.FieldByName('Function_ID').AsInteger; // don't ask me Y!
  tempResult.FunctionName := qry.FieldByName('Function_Name').AsString;
  tempResult.ReportToEmail := qry.FieldByName('Report_To_Email').AsString;
  tempResult.AuditLevel := qry.FieldByName('Audit_Level').AsInteger;
  tempResult.variableABC := qry.FieldByName('ABC').AsDateTime;
  result := tempResult;
  qry.Close;
end;

procedure TMyForm.PerformFunction(aFunctionID: integer; FRP: TFunctionRuntimeParameters);
var
  MyReportDocument: TMyReportDocument;
begin
  if (FRP.AuditLevel > 0) then
    // do something special before starting the function

  case aFunctionID of
    101: MyReportDocument := DoBusinessFunctionABC;
    102: MyReportDocument := DoBusinessFunctionDEF;
    103: MyReportDocument := DoBusinessFunctionXYZ;
  end;

  SendReportDocumentByEmailTo(MyReportDocument, FRP.ReportToEmail);

  if ((Now - FRP.variableABC) > 28) then
    // perhaps do something special every 4 weeks!

  if (FRP.AuditLevel > 0) then
    // do something special after the function has finished
end;

procedure TMyForm.btnBusinessFunctionXYZClick(Sender: TObject);
var
  FunctionID: integer;
  FunctionRuntimeParameters: TFunctionRuntimeParameters; // record that mimics db entry
begin
  FunctionID := 1234; // or you might prefer an enum
  FunctionRuntimeParameters := GetFunctionRuntimeParametersFromDB(FunctionID);
  PerformFunction(FunctionID, FunctionRuntimeParameters);
end;

This way as the Runtime Parameters change in the database, the application will behave differently without the need for recompilation.

like image 189
Sam Avatar answered Nov 09 '22 11:11

Sam