SlideShare una empresa de Scribd logo
1 de 61
Descargar para leer sin conexión
Dr. Jenkins y Mr. Hyde
Acto I - Los personajes
Acto II - Envuelvelo en una API "REST"
Acto III - Dos en uno
Acto IV - Sangre, sudor y migraciones
Acto V - URL's y Redis
desymfony 2013 Dr. Jenkins & Mr. Hyde
ACTO I - Los personajes
desymfony 2013 Dr. Jenkins & Mr. Hyde
Mr. Hyde
Bodaclick
desymfony 2013 Dr. Jenkins & Mr. Hyde
• PHP 4 Spaghetti western
• Ausencia MVC
• Inicio del desarrollo en 2000
• Reescritura del 90% en 2007
• + de 25 desarrolladores
• 4 Bases de datos
• Más de 1.5M de líneas de código
• Formado por: Directorio, CRM+ERP, Lista de bodas,
Estadisticas, Extranet para clientes, CMS, Área de
contenidos, WebTV, Etc.
Mr. Hyde
desymfony 2013 Dr. Jenkins & Mr. Hyde
desymfony 2013 Dr. Jenkins & Mr. Hyde
Dr. Jenkins
Core
desymfony 2013 Dr. Jenkins & Mr. Hyde
• REST
• PHP 5.4
• Symfony 2.1.x
• LAM
• SQLite
• Redis con Twemproxy
• MongoDB
• RabbitMQ
• Jenkins, PHPUnit & Capifony
• Satis
Dr. Jenkins
desymfony 2013 Dr. Jenkins & Mr. Hyde
Joel Spolsky (Stack Overflow co-founder) dijo:
(sobre Netscape)" Bueno, si. Lo hicieron. Lo hicieron al tomar la peor
decisión estratégica que una empresa de software puede hacer:
decidieron re-escribir el código desde 0"
fuente: http://www.joelonsoftware.com/articles/fog0000000069.html
Dan Milsten, fundador de Hub8, en un post en On Startups (publicado
por Dharmesh Shah, inversor de Stack Exchange):
"Prepárate para que este proyecto no termine jamás.Lo primero y
absolutamente crítico que tienes que entender sobre empezar una
reescritura es que va a tomar muchísimo más de lo que esperas. Incluso
después de que quitas el típico optimismo del desarrollador. He aquí
porqué: Migrar datos es lo peor que puedes echarte a la cara, más allá
de cualquier otra cosa.“
fuente: http://onstartups.com/tabid/3339/bid/97052/How-To-Survive-a-Ground-Up-Rewrite-Without-Losing-Your-Sanity.aspx
Re-escribir desde 0, según los expertos
desymfony 2013 Dr. Jenkins & Mr. Hyde
ACTO II - Envuelvelo en una API "REST"
desymfony 2013 Dr. Jenkins & Mr. Hyde
Entidades
Sobrenaturales
desymfony 2013 Dr. Jenkins & Mr. Hyde
Las bases de datos
BBDD 1
BBDD 3 BBDD 4
BBDD 2
desymfony 2013 Dr. Jenkins & Mr. Hyde
BBDD 1
BBDD 3 BBDD 4
Relacionadas entre si por claves extranjeras mantenidas por software
//namespace BDKLegacyDbBundleEntity;
/**
* @ORMTable(name="boda.CLIENTE")
* @ORMEntity
*/
class Cliente
{
//...
/**
* @ORMManyToMany(targetEntity="Tags")
* @ORMJoinTable(name="bodamoll.b_tags",
* joinColumns={@ORMJoinColumn(name="id_cliente", referencedColumnName="ID")},
* inverseJoinColumns={@ORMJoinColumn(name="id_tag", referencedColumnName="id")}
* )
*/
private $id_tag;
//...
Hackeando las DQL
desymfony 2013 Dr. Jenkins & Mr. Hyde
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
if ($this->kernel->getEnvironment() != 'test') {
return;
}
$classMetadata = $eventArgs->getClassMetadata();
$assoMap = $classMetadata->getAssociationMappings();
foreach ($assoMap as $asso) {
if (isset($asso["joinTable"])) {
$asso["joinTable"]["name"] = str_replace(".", "_", $asso["joinTable"]["name"]);
$classMetadata->setAssociationOverride($asso["fieldName"],$asso);
}
}
$tableName = $classMetadata->getTableName();
$classMetadata->setPrimaryTable(array('name' => str_replace(".", "_", $tableName)));
}
Hackeando las DQL
desymfony 2013 Dr. Jenkins & Mr. Hyde
desymfony 2013 Dr. Jenkins & Mr. Hyde
Un Jenkins feliz
API Legacy
.../api/[public|secured]/legacy/...
desymfony 2013 Dr. Jenkins & Mr. Hyde
//namespace BDKLegacyBundleTestsControllerLegacyController;
//PostPublicUserControllerTest
$user = [
'legacy' => [...],
'core' => [...],
];
$client->request('POST', "/api/public/legacy/novio{$oauthString}",
$user, array());
API Legacy - Envío
desymfony 2013 Dr. Jenkins & Mr. Hyde
//namespace BDKLegacyBundleController;
// class UserPublicController
public function postNovioAction(Request $request)
{
$view = FOSView::create();
$viewData = $this->container
->get('bdk.legacy.rest_manager')
->postUser($request, ProfileType::USER);
return $view->setStatusCode($viewData['status'])
->setData($viewData['data']);
}
API Legacy - Recepción
desymfony 2013 Dr. Jenkins & Mr. Hyde
API Bridge
.../api/[public|secured]/...
desymfony 2013 Dr. Jenkins & Mr. Hyde
//Legacy
$user = [
"legacy" => [...],
"core" => [...],
];
//Bridge
$user = ["name" = "", "surname"= "", .... ];
$client->request('POST', "/api/public/users{$this-> oauthString}&profile=user",
$user, array());
API Legacy - Envío
desymfony 2013 Dr. Jenkins & Mr. Hyde
// namespace BDKLegacyBundleController;
// class BridgeUserPublicController
public function postUsersAction(Request $request, $profile)
{
$em = $this->get('doctrine.orm.legacy_entity_manager');
//...
$mapper = new CoreArrayToLegacyNovioArrayMapper($em);
$coreArray = $request->request->all();
$params = [
'core' => $coreArray,
'legacy' => $mapper->map($coreArray)
];
$request->request->replace($params);
$view = FOSView::create();
$viewData = $this->container->get('bdk.legacy.rest_manager')
->postUser($request, ProfileType::USER);
return $view->setStatusCode($viewData['status'])->setData($viewData['data']);
}
API Legacy - Recepción
desymfony 2013 Dr. Jenkins & Mr. Hyde
ACTO III - Dos en uno
desymfony 2013 Dr. Jenkins & Mr. Hyde
Frontal
login
único
App.
nueva
App.
antigua
Perfil del
usuario
OAuth
Login
desymfony 2013 Dr. Jenkins & Mr. Hyde
Frontal
login
único
App.
nueva
App.
antigua
Perfil del
usuario
OAuth + Token WSSE (login)
Login
desymfony 2013 Dr. Jenkins & Mr. Hyde
Frontal
login
único
App.
nueva
App.
antigua
Perfil del
usuario
Info del
usuario
Acceso al perfil del usuario
OAuth + Token WSSE (login)
Login
desymfony 2013 Dr. Jenkins & Mr. Hyde
Perfil del
usuario
Enlaces a la plataforma antigua
Login
Frontal
login
único
App.
nueva
App.
antigua
Perfil del
usuario
Info del
usuario
OAuth + Token WSSE (login)
Acceso al perfil del usuario
desymfony 2013 Dr. Jenkins & Mr. Hyde
Internet Reverse
Proxy
Perfil del usuario - Reverse Proxy
bodaclick.com/^((?!my).)*$
bodaclick.com/my
desymfony 2013 Dr. Jenkins & Mr. Hyde
desymfony 2013 Dr. Jenkins & Mr. Hyde
Perfil del usuario - Reverse Proxy
desymfony 2013 Dr. Jenkins & Mr. Hyde
Internet Reverse
Proxy
bodaclick.com/^((?!my).)*$
bodaclick.com/my
iframe
ACTO IV - Sangre, sudor y migraciones
desymfony 2013 Dr. Jenkins & Mr. Hyde
• 4 bases de datos
• Datos inconsistentes
• Emails repetidos
• Fechas como 0000-00-00
• Enums
• Tenemos tablas con más de 100 campos
• Campos por defecto a 0000-00-00
• Tablas tanto innodb como MyISAM
• Cotejamientos diferentes (utf8, latin)
• Tablas > 6 GB
API Legacy - Mapeo
desymfony 2013 Dr. Jenkins & Mr. Hyde
desymfony 2013 Dr. Jenkins & Mr. Hyde
Fuerza bruta
Migraciones
desymfony 2013 Dr. Jenkins & Mr. Hyde
protected function migrateUser($override, $limit = 0)
{
//...
foreach ($oldUsers as $oldUser) {
try {
$userArray = $mapper->getUserCoreArray($oldUser);
$userArray = $this->trimUserArray($userArray);
$coreUser = $this->persistToCore($userArray, $oldUser, $override);
$oldUser->setNewId($coreUser->getUser()->getId());
$this->legacyEm->persist($oldUser);
$this->legacyEm->flush();
} catch (Exception $e) {
//...
}
}
}
Migración por comando único
desymfony 2013 Dr. Jenkins & Mr. Hyde
Divididas
Migraciones
desymfony 2013 Dr. Jenkins & Mr. Hyde
MigrateUserCommand RabbitMQ
Consumidor de Mapeos
Datos mapeados
Migración en dos pasos
desymfony 2013 Dr. Jenkins & Mr. Hyde
MySQL MySQL
App. nueva App. antigua
protected function migrateUser($override, $limit = 0)
{
//...
foreach ($oldUsers as $oldUser) {
try {
$userArray = $mapper->getUserCoreArray($oldUser);
//Usamos RabbitMQ
$data['oldUser'] = $oldUser;
$data['userArray'] = $userArray;
$this->getContainer()->get('old_sound_rabbit_mq.migration_producer')
->publish($serializer($data));
} catch (Exception $e) {
//...
}
}
//...
}
Migración por comando y consumidor
desymfony 2013 Dr. Jenkins & Mr. Hyde
protected function execute(AMQPMessage $migration)
{
//...
try {
$coreUser = $this->persistToCore($userArray, $oldUser, $override);
$oldUser->setNewId($coreUser->getUser()->getId());
$this->legacyEm->persist($oldUser);
$this->legacyEm->flush();
} catch (Exception $e) {
//...
}
//...
}
Migración por comando y consumidor
desymfony 2013 Dr. Jenkins & Mr. Hyde
Perezosas
Migraciones
desymfony 2013 Dr. Jenkins & Mr. Hyde
class SecurityController extends Controller
{
public function postTokenAction()
{
//...
$username = $request->get('_username');
$password = $request->get('_password');
$preLoginEvent = new PreUserLoginEvent($username,
$password);
$this->get('event_dispatcher')
->dispatch(UserEvents::PRE_LOGIN, $preLoginEvent);
//...
}
}
Migración por evento - Lanzamiento
desymfony 2013 Dr. Jenkins & Mr. Hyde
...Perezosas (Listener)//namespace BDKLegacyBundleEventListener;
//class UserLoginCreateUserProfileListener
public function onPreUserLogin(PreUserLoginEvent $event)
{
//...
$coreUser = $this->legacyUserManager->
createCoreUser(
$userArray,
ProfileType::USER,
['Create', 'Default']
);
//...
}
Migración por evento - Listener
desymfony 2013 Dr. Jenkins & Mr. Hyde
Perezosas,
asíncronas e
inversas
Migraciones
desymfony 2013 Dr. Jenkins & Mr. Hyde
Evento asíncrono comunicado al driver
AsyncEventDispatcher
Controlador
Evento
propio
desymfony 2013 Dr. Jenkins & Mr. Hyde
//abstract class AsyncWeddingEvent implements AsyncEventInterface;
//class PostCreatWeddingEvent extends AsyncWeddingEvent;
$event = new PostCreateWeddingEvent();
$event->setWedding($wedding);
$event->setUserProfile($userProfile);
$this->container->get('bdk.async_event_dispatcher')->dispatch($event);
Evento asíncrono lanzado
desymfony 2013 Dr. Jenkins & Mr. Hyde
Evento asíncrono comunicado al driver
Listener/
Publicador
o Driver
AsyncEventDispatcher
Controlador
Evento
propio
desymfony 2013 Dr. Jenkins & Mr. Hyde
Evento asíncrono para un sistema pub/sub
Sistema Pub/Sub
Listener/
Publicador
o Driver
AsyncEventDispatcher
Controlador
Evento
propio
desymfony 2013 Dr. Jenkins & Mr. Hyde
//Resources/config/async_drivers.yml
services:
bdk.wedding.async_event_driver_create:
class: BDKWeddingBundleModelEventDispatcherAsyncDriverRabbitMQDriver
arguments: [@old_sound_rabbit_mq.wedding_event_producer, @serializer]
calls:
- [setRoutingKey, ['create.wedding.event']]
tags:
- { name: bdk.async_event_dispatcher, event: bdk.async.post_create_wedding }
Configuración del driver
desymfony 2013 Dr. Jenkins & Mr. Hyde
Evento asíncrono para un sistema pub/sub
Sistema Pub/Sub
Subscriptor MySQLMySQL
Listener/
Publicador
o Driver
AsyncEventDispatcher
Controlador
Evento
propio
desymfony 2013 Dr. Jenkins & Mr. Hyde
App. nueva App. antigua
Evento asíncrono para un sistema pub/sub
Consumidor MySQLMySQL
Listener/
Productor
o Driver
AsyncEventDispatcher
Controlador
Evento
propio
desymfony 2013 Dr. Jenkins & Mr. Hyde
*.wedding.event
RabbitMQ
Topic Exchange
App. nueva App. antigua
//BDKLegacyBundleEventListenerAsyncWeddingEventListener
class WeddingEventListener implements ConsumerInterface
{
//...
if ($wedding->getProvince()) {
$legacyCountry = $this->legacyEm->getRepository('BDKLegacyDbBundle:Pais')
->findOneByCodPais($wedding->getProvince()->getCountry());
$legacyProvince = $this->legacyEm->getRepository('BDKLegacyDbBundle:Provincia')
->findOneBy(['idPais' => $legacyCountry->getId(), 'provincia' =>
$wedding->getProvince()->getName()]);
$legacyEventUser->setProvinciaId($legacyProvince->getId());
}
//...
}
Consumidor
desymfony 2013 Dr. Jenkins & Mr. Hyde
Mixtas
Migraciones
desymfony 2013 Dr. Jenkins & Mr. Hyde
Migración perezosa mixta
MongoDb
MySQL
MySQL
Legacy
Internet
Internet
desymfony 2013 Dr. Jenkins & Mr. Hyde
Perfil del
usuario
App. antigua
App. nueva
//postLoad
//...
$service = $em->getClassMetadata('BDKWeddingBundle:Wedding')
->reflClass->getProperty(service);
$service>setAccessible(true);
$service>setValue(
$wedding,
$this->dm->getReference(
'BDKWeddingBundle:Service',
$wedding->getServiced()
)
);
Cargar datos desde MongoDB
desymfony 2013 Dr. Jenkins & Mr. Hyde
ACTO V - Routing
desymfony 2013 Dr. Jenkins & Mr. Hyde
/var/.../reportajes/45.php
//45.php
$ruta = "/desymfony-2013.html";
//...
Urls físicas
desymfony 2013 Dr. Jenkins & Mr. Hyde
bodaclick.com/desymfony-2013.html
//desymfony-2013.html
$idReportaje = 45;
cargaContenido();
//...
<a href=‘url(“reportaje=45”)’> Texto </a>
Urls via redis
SET www.bodaclick.com:reportaje:45
/desymfony-2013.html
desymfony 2013 Dr. Jenkins & Mr. Hyde
<a href=‘url(“reportaje=45”)’> Texto </a> bodaclick.com/desymfony-2013.html
//desymfony-2013.html
$idReportaje = 45;
cargaContenido();
//...
desymfony 2013 Dr. Jenkins & Mr. Hyde
@etorras79
etorras
@BodaclickIT
Enrique Torras, como Mr. Hyde
• Ingeniero en Informática
• Desarrollando web desde 2004
• Actualmente dirige el área de
desarrollo en Bodaclick
desymfony 2013 Dr. Jenkins & Mr. Hyde
slideshare.net/etorras
@egulias
egulias
• Desarrollador web desde 2006
• Coqueteando con Symfony (y
otros frameworks) desde 2007
• Miembro de Symfony Madrid
• Actualmente trabajando como
líder de equipo en Bodaclick
@BodaclickIT
Eduardo Gulias, como Dr. Jenkins
slideshare.net/egulias
joind.in/talk/view/8834
desymfony 2013 Dr. Jenkins & Mr. Hyde
¿?
desymfony 2013 Dr. Jenkins & Mr. Hyde

Más contenido relacionado

Similar a De symfony 2013 dr. jenkins y mr. hyde - slides-842359017

Symfony parte 15 Consultas y Migración
Symfony parte 15 Consultas y MigraciónSymfony parte 15 Consultas y Migración
Symfony parte 15 Consultas y MigraciónRodrigo Miranda
 
Codemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipsterCodemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipsterAdolfo Sanz De Diego
 
Introducción a AngularJS
Introducción a AngularJS Introducción a AngularJS
Introducción a AngularJS Marcos Reynoso
 
Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...
Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...
Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...Enrique Catala Bañuls
 
Reportes En J Developer Parte 1 Y 2
Reportes En J Developer   Parte 1 Y 2Reportes En J Developer   Parte 1 Y 2
Reportes En J Developer Parte 1 Y 2Steven Gomez
 
CouchDB y el desarrollo de aplicaciones Android
CouchDB y el desarrollo de aplicaciones AndroidCouchDB y el desarrollo de aplicaciones Android
CouchDB y el desarrollo de aplicaciones AndroidRicardo Monagas Medina
 
Drupal7 para desarrolladores
Drupal7 para desarrolladoresDrupal7 para desarrolladores
Drupal7 para desarrolladoresPedro Cambra
 
Open Source Modern Web Development
Open Source Modern Web DevelopmentOpen Source Modern Web Development
Open Source Modern Web DevelopmentJaime Irurzun
 
Inyecciones sql para todos
Inyecciones sql para todosInyecciones sql para todos
Inyecciones sql para todoscsaralg
 
Deployer PHP. Presentación para #PHPSevilla
Deployer PHP. Presentación para #PHPSevillaDeployer PHP. Presentación para #PHPSevilla
Deployer PHP. Presentación para #PHPSevillaAgencia INNN
 
Taller desarrollo de apis
Taller desarrollo de apisTaller desarrollo de apis
Taller desarrollo de apisCloudAppi
 
Desarrollo de aplicaciones web usando Catalyst y jQuery
Desarrollo de aplicaciones web usando Catalyst y jQueryDesarrollo de aplicaciones web usando Catalyst y jQuery
Desarrollo de aplicaciones web usando Catalyst y jQueryJavier P.
 

Similar a De symfony 2013 dr. jenkins y mr. hyde - slides-842359017 (20)

Doctrine2 sf2Vigo
Doctrine2 sf2VigoDoctrine2 sf2Vigo
Doctrine2 sf2Vigo
 
Symfony parte 15 Consultas y Migración
Symfony parte 15 Consultas y MigraciónSymfony parte 15 Consultas y Migración
Symfony parte 15 Consultas y Migración
 
Codemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipsterCodemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipster
 
Introducción a AngularJS
Introducción a AngularJS Introducción a AngularJS
Introducción a AngularJS
 
Servicios web
Servicios webServicios web
Servicios web
 
Servicios web
Servicios webServicios web
Servicios web
 
Segunda sesion
Segunda sesionSegunda sesion
Segunda sesion
 
Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...
Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...
Capas de acceso a datos .NET escalables de verdad: el batido perfecto para el...
 
Curso AngularJS - 7. temas avanzados
Curso AngularJS - 7. temas avanzadosCurso AngularJS - 7. temas avanzados
Curso AngularJS - 7. temas avanzados
 
Reportes En J Developer Parte 1 Y 2
Reportes En J Developer   Parte 1 Y 2Reportes En J Developer   Parte 1 Y 2
Reportes En J Developer Parte 1 Y 2
 
CouchDB y el desarrollo de aplicaciones Android
CouchDB y el desarrollo de aplicaciones AndroidCouchDB y el desarrollo de aplicaciones Android
CouchDB y el desarrollo de aplicaciones Android
 
Drupal7 para desarrolladores
Drupal7 para desarrolladoresDrupal7 para desarrolladores
Drupal7 para desarrolladores
 
Open Source Modern Web Development
Open Source Modern Web DevelopmentOpen Source Modern Web Development
Open Source Modern Web Development
 
Servicios web
Servicios webServicios web
Servicios web
 
Inyecciones sql para todos
Inyecciones sql para todosInyecciones sql para todos
Inyecciones sql para todos
 
Jquery para principianes
Jquery para principianesJquery para principianes
Jquery para principianes
 
J M E R L I N P H P
J M E R L I N P H PJ M E R L I N P H P
J M E R L I N P H P
 
Deployer PHP. Presentación para #PHPSevilla
Deployer PHP. Presentación para #PHPSevillaDeployer PHP. Presentación para #PHPSevilla
Deployer PHP. Presentación para #PHPSevilla
 
Taller desarrollo de apis
Taller desarrollo de apisTaller desarrollo de apis
Taller desarrollo de apis
 
Desarrollo de aplicaciones web usando Catalyst y jQuery
Desarrollo de aplicaciones web usando Catalyst y jQueryDesarrollo de aplicaciones web usando Catalyst y jQuery
Desarrollo de aplicaciones web usando Catalyst y jQuery
 

De symfony 2013 dr. jenkins y mr. hyde - slides-842359017

  • 1.
  • 2.
  • 3. Dr. Jenkins y Mr. Hyde Acto I - Los personajes Acto II - Envuelvelo en una API "REST" Acto III - Dos en uno Acto IV - Sangre, sudor y migraciones Acto V - URL's y Redis desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 4. ACTO I - Los personajes desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 5. Mr. Hyde Bodaclick desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 6. • PHP 4 Spaghetti western • Ausencia MVC • Inicio del desarrollo en 2000 • Reescritura del 90% en 2007 • + de 25 desarrolladores • 4 Bases de datos • Más de 1.5M de líneas de código • Formado por: Directorio, CRM+ERP, Lista de bodas, Estadisticas, Extranet para clientes, CMS, Área de contenidos, WebTV, Etc. Mr. Hyde desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 7. desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 8. Dr. Jenkins Core desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 9. • REST • PHP 5.4 • Symfony 2.1.x • LAM • SQLite • Redis con Twemproxy • MongoDB • RabbitMQ • Jenkins, PHPUnit & Capifony • Satis Dr. Jenkins desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 10. Joel Spolsky (Stack Overflow co-founder) dijo: (sobre Netscape)" Bueno, si. Lo hicieron. Lo hicieron al tomar la peor decisión estratégica que una empresa de software puede hacer: decidieron re-escribir el código desde 0" fuente: http://www.joelonsoftware.com/articles/fog0000000069.html Dan Milsten, fundador de Hub8, en un post en On Startups (publicado por Dharmesh Shah, inversor de Stack Exchange): "Prepárate para que este proyecto no termine jamás.Lo primero y absolutamente crítico que tienes que entender sobre empezar una reescritura es que va a tomar muchísimo más de lo que esperas. Incluso después de que quitas el típico optimismo del desarrollador. He aquí porqué: Migrar datos es lo peor que puedes echarte a la cara, más allá de cualquier otra cosa.“ fuente: http://onstartups.com/tabid/3339/bid/97052/How-To-Survive-a-Ground-Up-Rewrite-Without-Losing-Your-Sanity.aspx Re-escribir desde 0, según los expertos desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 11. ACTO II - Envuelvelo en una API "REST" desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 13. Las bases de datos BBDD 1 BBDD 3 BBDD 4 BBDD 2 desymfony 2013 Dr. Jenkins & Mr. Hyde BBDD 1 BBDD 3 BBDD 4 Relacionadas entre si por claves extranjeras mantenidas por software
  • 14. //namespace BDKLegacyDbBundleEntity; /** * @ORMTable(name="boda.CLIENTE") * @ORMEntity */ class Cliente { //... /** * @ORMManyToMany(targetEntity="Tags") * @ORMJoinTable(name="bodamoll.b_tags", * joinColumns={@ORMJoinColumn(name="id_cliente", referencedColumnName="ID")}, * inverseJoinColumns={@ORMJoinColumn(name="id_tag", referencedColumnName="id")} * ) */ private $id_tag; //... Hackeando las DQL desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 15. public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) { if ($this->kernel->getEnvironment() != 'test') { return; } $classMetadata = $eventArgs->getClassMetadata(); $assoMap = $classMetadata->getAssociationMappings(); foreach ($assoMap as $asso) { if (isset($asso["joinTable"])) { $asso["joinTable"]["name"] = str_replace(".", "_", $asso["joinTable"]["name"]); $classMetadata->setAssociationOverride($asso["fieldName"],$asso); } } $tableName = $classMetadata->getTableName(); $classMetadata->setPrimaryTable(array('name' => str_replace(".", "_", $tableName))); } Hackeando las DQL desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 16. desymfony 2013 Dr. Jenkins & Mr. Hyde Un Jenkins feliz
  • 18. //namespace BDKLegacyBundleTestsControllerLegacyController; //PostPublicUserControllerTest $user = [ 'legacy' => [...], 'core' => [...], ]; $client->request('POST', "/api/public/legacy/novio{$oauthString}", $user, array()); API Legacy - Envío desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 19. //namespace BDKLegacyBundleController; // class UserPublicController public function postNovioAction(Request $request) { $view = FOSView::create(); $viewData = $this->container ->get('bdk.legacy.rest_manager') ->postUser($request, ProfileType::USER); return $view->setStatusCode($viewData['status']) ->setData($viewData['data']); } API Legacy - Recepción desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 21. //Legacy $user = [ "legacy" => [...], "core" => [...], ]; //Bridge $user = ["name" = "", "surname"= "", .... ]; $client->request('POST', "/api/public/users{$this-> oauthString}&profile=user", $user, array()); API Legacy - Envío desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 22. // namespace BDKLegacyBundleController; // class BridgeUserPublicController public function postUsersAction(Request $request, $profile) { $em = $this->get('doctrine.orm.legacy_entity_manager'); //... $mapper = new CoreArrayToLegacyNovioArrayMapper($em); $coreArray = $request->request->all(); $params = [ 'core' => $coreArray, 'legacy' => $mapper->map($coreArray) ]; $request->request->replace($params); $view = FOSView::create(); $viewData = $this->container->get('bdk.legacy.rest_manager') ->postUser($request, ProfileType::USER); return $view->setStatusCode($viewData['status'])->setData($viewData['data']); } API Legacy - Recepción desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 23. ACTO III - Dos en uno desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 25. Frontal login único App. nueva App. antigua Perfil del usuario OAuth + Token WSSE (login) Login desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 26. Frontal login único App. nueva App. antigua Perfil del usuario Info del usuario Acceso al perfil del usuario OAuth + Token WSSE (login) Login desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 27. Perfil del usuario Enlaces a la plataforma antigua Login Frontal login único App. nueva App. antigua Perfil del usuario Info del usuario OAuth + Token WSSE (login) Acceso al perfil del usuario desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 28. Internet Reverse Proxy Perfil del usuario - Reverse Proxy bodaclick.com/^((?!my).)*$ bodaclick.com/my desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 29. desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 30. Perfil del usuario - Reverse Proxy desymfony 2013 Dr. Jenkins & Mr. Hyde Internet Reverse Proxy bodaclick.com/^((?!my).)*$ bodaclick.com/my iframe
  • 31. ACTO IV - Sangre, sudor y migraciones desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 32. • 4 bases de datos • Datos inconsistentes • Emails repetidos • Fechas como 0000-00-00 • Enums • Tenemos tablas con más de 100 campos • Campos por defecto a 0000-00-00 • Tablas tanto innodb como MyISAM • Cotejamientos diferentes (utf8, latin) • Tablas > 6 GB API Legacy - Mapeo desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 33. desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 34. Fuerza bruta Migraciones desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 35. protected function migrateUser($override, $limit = 0) { //... foreach ($oldUsers as $oldUser) { try { $userArray = $mapper->getUserCoreArray($oldUser); $userArray = $this->trimUserArray($userArray); $coreUser = $this->persistToCore($userArray, $oldUser, $override); $oldUser->setNewId($coreUser->getUser()->getId()); $this->legacyEm->persist($oldUser); $this->legacyEm->flush(); } catch (Exception $e) { //... } } } Migración por comando único desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 37. MigrateUserCommand RabbitMQ Consumidor de Mapeos Datos mapeados Migración en dos pasos desymfony 2013 Dr. Jenkins & Mr. Hyde MySQL MySQL App. nueva App. antigua
  • 38. protected function migrateUser($override, $limit = 0) { //... foreach ($oldUsers as $oldUser) { try { $userArray = $mapper->getUserCoreArray($oldUser); //Usamos RabbitMQ $data['oldUser'] = $oldUser; $data['userArray'] = $userArray; $this->getContainer()->get('old_sound_rabbit_mq.migration_producer') ->publish($serializer($data)); } catch (Exception $e) { //... } } //... } Migración por comando y consumidor desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 39. protected function execute(AMQPMessage $migration) { //... try { $coreUser = $this->persistToCore($userArray, $oldUser, $override); $oldUser->setNewId($coreUser->getUser()->getId()); $this->legacyEm->persist($oldUser); $this->legacyEm->flush(); } catch (Exception $e) { //... } //... } Migración por comando y consumidor desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 41. class SecurityController extends Controller { public function postTokenAction() { //... $username = $request->get('_username'); $password = $request->get('_password'); $preLoginEvent = new PreUserLoginEvent($username, $password); $this->get('event_dispatcher') ->dispatch(UserEvents::PRE_LOGIN, $preLoginEvent); //... } } Migración por evento - Lanzamiento desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 42. ...Perezosas (Listener)//namespace BDKLegacyBundleEventListener; //class UserLoginCreateUserProfileListener public function onPreUserLogin(PreUserLoginEvent $event) { //... $coreUser = $this->legacyUserManager-> createCoreUser( $userArray, ProfileType::USER, ['Create', 'Default'] ); //... } Migración por evento - Listener desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 44. Evento asíncrono comunicado al driver AsyncEventDispatcher Controlador Evento propio desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 45. //abstract class AsyncWeddingEvent implements AsyncEventInterface; //class PostCreatWeddingEvent extends AsyncWeddingEvent; $event = new PostCreateWeddingEvent(); $event->setWedding($wedding); $event->setUserProfile($userProfile); $this->container->get('bdk.async_event_dispatcher')->dispatch($event); Evento asíncrono lanzado desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 46. Evento asíncrono comunicado al driver Listener/ Publicador o Driver AsyncEventDispatcher Controlador Evento propio desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 47. Evento asíncrono para un sistema pub/sub Sistema Pub/Sub Listener/ Publicador o Driver AsyncEventDispatcher Controlador Evento propio desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 48. //Resources/config/async_drivers.yml services: bdk.wedding.async_event_driver_create: class: BDKWeddingBundleModelEventDispatcherAsyncDriverRabbitMQDriver arguments: [@old_sound_rabbit_mq.wedding_event_producer, @serializer] calls: - [setRoutingKey, ['create.wedding.event']] tags: - { name: bdk.async_event_dispatcher, event: bdk.async.post_create_wedding } Configuración del driver desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 49. Evento asíncrono para un sistema pub/sub Sistema Pub/Sub Subscriptor MySQLMySQL Listener/ Publicador o Driver AsyncEventDispatcher Controlador Evento propio desymfony 2013 Dr. Jenkins & Mr. Hyde App. nueva App. antigua
  • 50. Evento asíncrono para un sistema pub/sub Consumidor MySQLMySQL Listener/ Productor o Driver AsyncEventDispatcher Controlador Evento propio desymfony 2013 Dr. Jenkins & Mr. Hyde *.wedding.event RabbitMQ Topic Exchange App. nueva App. antigua
  • 51. //BDKLegacyBundleEventListenerAsyncWeddingEventListener class WeddingEventListener implements ConsumerInterface { //... if ($wedding->getProvince()) { $legacyCountry = $this->legacyEm->getRepository('BDKLegacyDbBundle:Pais') ->findOneByCodPais($wedding->getProvince()->getCountry()); $legacyProvince = $this->legacyEm->getRepository('BDKLegacyDbBundle:Provincia') ->findOneBy(['idPais' => $legacyCountry->getId(), 'provincia' => $wedding->getProvince()->getName()]); $legacyEventUser->setProvinciaId($legacyProvince->getId()); } //... } Consumidor desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 53. Migración perezosa mixta MongoDb MySQL MySQL Legacy Internet Internet desymfony 2013 Dr. Jenkins & Mr. Hyde Perfil del usuario App. antigua App. nueva
  • 55. ACTO V - Routing desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 56. /var/.../reportajes/45.php //45.php $ruta = "/desymfony-2013.html"; //... Urls físicas desymfony 2013 Dr. Jenkins & Mr. Hyde bodaclick.com/desymfony-2013.html //desymfony-2013.html $idReportaje = 45; cargaContenido(); //... <a href=‘url(“reportaje=45”)’> Texto </a>
  • 57. Urls via redis SET www.bodaclick.com:reportaje:45 /desymfony-2013.html desymfony 2013 Dr. Jenkins & Mr. Hyde <a href=‘url(“reportaje=45”)’> Texto </a> bodaclick.com/desymfony-2013.html //desymfony-2013.html $idReportaje = 45; cargaContenido(); //...
  • 58. desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 59. @etorras79 etorras @BodaclickIT Enrique Torras, como Mr. Hyde • Ingeniero en Informática • Desarrollando web desde 2004 • Actualmente dirige el área de desarrollo en Bodaclick desymfony 2013 Dr. Jenkins & Mr. Hyde slideshare.net/etorras
  • 60. @egulias egulias • Desarrollador web desde 2006 • Coqueteando con Symfony (y otros frameworks) desde 2007 • Miembro de Symfony Madrid • Actualmente trabajando como líder de equipo en Bodaclick @BodaclickIT Eduardo Gulias, como Dr. Jenkins slideshare.net/egulias joind.in/talk/view/8834 desymfony 2013 Dr. Jenkins & Mr. Hyde
  • 61. ¿? desymfony 2013 Dr. Jenkins & Mr. Hyde