Skip to content

Commit

Permalink
Merge pull request #54 from atk4/feature/review-20160719
Browse files Browse the repository at this point in the history
Review 2016-07-19
  • Loading branch information
romaninsh committed Jul 20, 2016
2 parents 6e7409a + cc57425 commit 4c66bf2
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 110 deletions.
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**PHP Framework for better Business Logic design and scalable database access.**

Use Agile Data inside your existing PHP application (works with most frameworks) to define and map your business logic into your database schema. Agile Data is designed with fresh ideas to solve efficiency, performance, clarity, testability and cross-database compatibility in medium and large PHP projects.
Use Agile Data inside your existing PHP application (works with most frameworks) to define and map your business logic into your database schema. Agile Data is designed with fresh ideas how to solve efficiency, performance, clarity, testability and cross-database compatibility in medium and large PHP projects.

Code Quality:

Expand All @@ -25,7 +25,7 @@ Stats:

## Goals and Features

Agile Data is a comprehensive framework for use in SaaS and Enterprise PHP, that focuses on solving these major goals:
Agile Data is a comprehensive framework for use in SaaS and Enterprise PHP projects, that focuses on solving these major goals:

### 1. Object-oriented Business Logic and Persistence mapping

Expand Down Expand Up @@ -61,22 +61,24 @@ $clients->addCondition('is_vip', true);
$p = $clients->ref('Order')->ref('Payment');
```

In the code snippet above, `$p` will be a model object with containing all payments of all orders placed by VIP clients in scope. Traversal executes no queries but rather relies on sub-query logic.
In the code snippet above, `$p` will be a model object containing all payments of all orders placed by VIP clients in scope. Traversal executes no queries but rather relies on sub-query logic.

### 4. Database Vendor Abstraction and Multi-record Actions

NoSQL databases are rapidly adding options to peform multi-record operations and aggregation. Agile Data basic operations, such as record manipulation, already works with NoSQL transparently. In addition to that, Actions introduce a unified interface that can be used across all supporting persistence drivers. Consider this as continuation of the example above:

``` php
// get count of all payments (see previous example for scope)
$cnt = $p->action('count')->getOne();

// delete all notifications for these payments
$n = new my\Model_Notification($db);
$n->addCondition('payment_id', $p->action('field', ['id']));

$n->action('delete')->execute();
```

When Action is executed or embedded, frameworks makes decision on how to best execute the strategy by using server-side capabilities of the database. If database is not capable of sub-select or multi-row operations, then it is still possible for Agile Data to simulate the action inside PHP.
When Action is executed or embedded, framework makes decision on how to best execute the strategy by using server-side capabilities of the database. If database is not capable of sub-select or multi-row operations, then it is still possible for Agile Data to simulate the action inside PHP.

The same business Model definition can work with multiple database types, making it easy to store your data in caches, session, files or access it through API. The next example shows example of database-agnostic code that will work with either MySQL or MongoDB:

Expand All @@ -95,7 +97,8 @@ if ($m->verifyPassword($pass)) {

### 5. Reducing number of queries

When using API of your own business logic, Agile Data gives you the ability to perform more operations, such as joins, expressions and more designed to reduce the number of queries and make them more efficient. My next example will create export of Clients along with their "account balance" that will be calculated within just a single query:
Business Logic designed with Agile Data can natively perform complex data operations such as joins, sub-selects, expressions which skilled developer can use to reduce total number of SQL queries per application request.
My next example will create export of Clients along with their "account balance" that will be calculated by just a single query:

``` php
$c = new my\Model_Client($db);
Expand All @@ -105,6 +108,10 @@ $c->getRef('Payment')->addField('payments', ['aggregate'=>'sum', 'field'=>'paid'
$c->addExpression('balance', '[purchases]-[payments]');

echo json_encode($c->export(['name','balance']));

// purchases = sum(order.total) for specific client
// payments = sum(payment.paid) for specific client
// balance = sum(order.total) - sum(payment.paid)
```

### 6. Manipulating Records
Expand All @@ -121,7 +128,7 @@ $p->save();
There are two significant advantages specifically designed to reduce data transfer footprint and improve security:

- you will only be able to load records from DataSet
- with onlyFields() you can specify which model fields you are looking to load
- with onlyFields() you can specify which model fields you are going to load

### 7. Business Model Aggregation

Expand All @@ -132,9 +139,9 @@ Most database mappers are good for accessing and modifying data only, however, A

### 8. Extensions and Customisation

Agile Data is a great framework but it can be further extended:
Agile Data already is a great framework, but it can be further extended:

- Add new database support, including NoSQL and custom RestAPI.
- Add new database driver support, including NoSQL and custom RestAPI
- Add new field types
- Add new relation types, including cross-database relations
- Validation engines
Expand All @@ -150,7 +157,7 @@ See section below to learn more about commercial services and support options.
### Getting Started Guides

* [Follow the Quick Start guides](http://agile-data.readthedocs.io/en/develop/quickstart.html)
* [Watch the quick video on Youtube](https://youtu.be/ZekgUxdPWwc)
* [Watch short introduction video on Youtube](https://youtu.be/ZekgUxdPWwc)

## Installing into existing project

Expand Down Expand Up @@ -217,7 +224,7 @@ Full documentation is available at [agile-core.readthedocs.io](http://agile-core

## Agile Toolkit

Agile Core is part of [Agile Toolkit - PHP UI Framework](http://agiletoolkit.org). If you like
Agile Data is part of [Agile Toolkit - PHP UI Framework](http://agiletoolkit.org). If you like
this project, you should also look into:

- [DSQL](https://github.com/atk4/dsql) - [![GitHub release](https://img.shields.io/github/release/atk4/dsql.svg?maxAge=2592000)]()
Expand Down
51 changes: 22 additions & 29 deletions docs/design.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Agile Data implements a fresh concepts that separates your Domain from persisten
cleanly yet manages to solve problems mentioned above.

The concepts implemented by Agile Data framework may require some getting used to
(especially if you used some traditional ORMs or Active Record implementations).
(especially if you used some traditional ORMs or Active Record implementations before).

Once you learn the concept behind Agile Data, you'll be able to write "Domain objects"
of your application with ease through a readable code and without impact on your
Expand Down Expand Up @@ -87,7 +87,7 @@ Some of your code will focus on working with Domain object without any concern a
"persistence". A good example is "Validation". When you Validate your Domain object
you just need to check field values, you would not even care where data came from.

Most of your code, however, will assume existence of SOME "persistence" but will not
Most of your code, however, will assume existence of SOME "persistence", but will not
rely on anything specific. Calculating total amount of your shopping basked price
is such an operation. Basket items are stored somewhere - array, SQL or NoSQL and
all you need is to calculate sum(amount). You don't even know how "amount" field
Expand All @@ -111,9 +111,9 @@ Domain Logic

When dealing with Domain logic, you work with a single object.

When we start a new aapp, we first decide on the Model structure.
When we start developing a new application, we first decide on the Model structure.
Think what models your application will use and how they are related. Do not
think in terms of "tables" but rather think in terms of "objects" and properties
think in terms of "tables", but rather think in terms of "objects" and properties
of those objects.

All of those model properties are "declared".
Expand Down Expand Up @@ -147,10 +147,10 @@ about object inheritance.

- User
- sendPasswordReminder()
- Client (extends Person)
- Client (extends User)
- register()
- checkout()
- Admin (extends Person)
- Admin (extends User)
- showAuditLog()
- Order

Expand All @@ -174,23 +174,17 @@ Domain Model Fields
Our next step is to define object fields (or properties). Remember that inheritance is at play here so you can take advantage of OOP:

- User
- name
- is_vip
- email
- password
- password_change_date
- name, is_vip, email, password, password_change_date
- Client
- phone
- Admin
- permission_level
- Order
- description
- amount
- is_paid
- description, amount, is_paid

Those fields are not just mere "properties" but have more some "meta" information behind them and that's why we call them "fields" and not "properties". A typical field contain information about field name, caption, type, validation rules, persistence rules, presentation rules and more. Meta information is optional and it can be used by automated processes (such as presentation or persistence).
Those fields are not just mere "properties", but have more "meta" information behind them and that's why we call them "fields" and not "properties". A typical field contain information about field name, caption, type, validation rules, persistence rules, presentation rules and more. Meta information is optional and it can be used by automated processes (such as presentation or persistence).

For instance is_paid has a `type('boolean')` which means it will be stored as 1/0 in MySQL but will use true/false in MongoDB. It will be displayed as a checkbox. Those decisions are made by the framework and will simplify your life, however if you want to do things differently, you will still be able to override default behaviour.
For instance, is_paid has a `type('boolean')` which means it will be stored as 1/0 in MySQL, but will use true/false in MongoDB. It will be displayed as a checkbox. Those decisions are made by the framework and will simplify your life, however if you want to do things differently, you will still be able to override default behaviour.

Code to declare fields::

Expand All @@ -211,7 +205,7 @@ Code to access field values::
Domain Model Relationship
-------------------------

Next - relations. Think how those object relate to each-other. Think in terms of "specific object" and not database relations. Client has many Orders. Order has one Client.
Next - relations. Think how those objects relate to each-other. Think in terms of "specific object" and not database relations. Client has many Orders. Order has one Client.

- User
- hasMany(Client)
Expand All @@ -235,7 +229,7 @@ Code (add inside `init()`)::
function init() {
parent::init();

$this->hasOne('User');
$this->hasOne('Client');

// addField declarations
}
Expand All @@ -246,7 +240,7 @@ Code (add inside `init()`)::
Persistence backed Domain Logic
===============================

Once we establish that Model objects are stored somewhere, we can start accessing them.
Once we establish that Model object and set its persistence layer, we can start accessing it.
Here is the code::

$order = new Model_Order();
Expand Down Expand Up @@ -276,13 +270,12 @@ Finally, some code may rely on specific features of your persistence layer.
Domain Model Expressions
------------------------

A final addition to our Domain Model are expressions. Those are the "formulas" where the value cannot be changed directly but is actually derived from other values.
A final addition to our Domain Model are expressions. Those are the "formulas" where the value cannot be changed directly, but is actually derived from other values.

- User
- is_password_expired
- Client
- amount_due
- total_order_amount
- amount_due, total_order_amount

Here field `is_password_expired` is the type of expression that is based on the field `password_change_date` and system date. In other words the value of this expression will be different depending on parameter outside of your app.

Expand Down Expand Up @@ -380,7 +373,7 @@ You can iterate over the DataSet::
You must remember that the code above will only create a single object and
iterating it will simply make it load different values.

At this point, I'll jump a head a bit and will show you an alternative code::
At this point, I'll jump ahead a bit and will show you an alternative code::

$sum = 0;
$sum = $db->add('Model_Order')->sum('amount')->getOne();
Expand Down Expand Up @@ -413,7 +406,7 @@ Related DataSets

Next, let's look on the orders of specific user. How would you load orders of a specific user.
Depending on your past experience you might think about "querying" Order table with condition
on user_id. We cant do that, because "query", "table" and "user_id" are persistence details
on user_id. We can't do that, because "query", "table" and "user_id" are persistence details
and we must keep them outside of business logic. Other ORM solution give you something like this::

$array_of_orders = $user->orders();
Expand All @@ -431,7 +424,7 @@ Agile Data implements traversal as a simple operation that converts one DataSet
The implementation of refSet is pretty powerful - $user_dataset can address 3 users in
the database and only 2 of those users are VIP. Typical ORM would require you to fetch all
VIP records and of then and perform queries to find their orders.
VIP records and then perform additional queries to find their orders.

Agile Data, however, perform traversal without accessing database at all. After `refSet()`
is executed, you have a new DataSet with a condition based on user sub-query. The actual
Expand Down Expand Up @@ -479,7 +472,7 @@ Finally the code above will work even if you use a simple Array as a data source
]]
]);

So getting back to the operation above, lets look at it in greater details::
So getting back to the operation above, lets look at it in more details::

$vip_order_count = $vip_orders->count()->getOne();

Expand All @@ -488,9 +481,9 @@ into persistence layer. However this method is returning a new object called "Ac
which is then executed when you call getOne().

Even though for a brief moment you had your hands on a "database-vendor specific" object,
you have immediately converted Action into an actual value - your code is universal and is not
persistence-specific. In Agile Data we permit code like that in our Domain Model and we call
it "Domain Model Action".
you have immediately converted Action into an actual value. As result your code is universal
and is not persistence-specific. In Agile Data we permit code like that in our Domain Model
and we call it "Domain Model Action".

Let me define this properly: Domain Model Action is an operation that can be executed
in your Domain Model layer which assumes existence of SOME Persistence for your model,
Expand Down
2 changes: 2 additions & 0 deletions docs/expressions.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@


.. _Expressions:

===========
Expressions
===========
Expand Down
13 changes: 7 additions & 6 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,18 @@ We educate developer about separating their Domain logic from persistence follow
best practices of enterprise software. We offer the solution that works really well for
most people and those who have extreme requirements can extend.

For example you can customize persistence logic of Data Model with your own query logic
For example, you can customize persistence logic of Data Model with your own query logic
where necessary.

Major Databases are Supported
=============================

The classic approach of record mapping puts low requirement on the database but sacrifice
performance. The abstraction of queries leads to your code being reliant on SQL vendor.
The classic approach of record mapping puts low requirements on the database, but as
result acrifice performance. The abstraction of queries leads to your code being reliant
on SQL vendor.

Agile Data introduces concepts that can be implemented across multiple database vendors
regardless of their support for SQL.
regardless of their support for SQL. Agile Data even works with NoSQL databases like MongoDB.

If you use SQL vendor, the standard operations will be more efficient, but if you operate
with a very basic database such as MemCache, then you can still simulate basic functionality.
Expand All @@ -62,9 +63,9 @@ basic functionality that you would normally need, but there are more awesome thi
that are built as extensions:

- join support - Map your Business Model to multiple tables
- aggregate models - Build your Report Models on top of Domain models, forget about custom queries
- aggregate models - Build your Report Models on top of Domain models and forget about custom queries
- more vendors - Add support for more vendors and your existing application code will just work
- validation - Perform validation on Domain level
- validation - Perform data validation on Domain level

Great for UI Frameworks
=======================
Expand Down

0 comments on commit 4c66bf2

Please sign in to comment.